Changeset 12127


Ignore:
Timestamp:
Mar 11, 2011, 4:19:01 AM (11 years ago)
Author:
jordan
Message:

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

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

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

Location:
trunk
Files:
2 added
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/libtransmission/Makefile.am

    r12074 r12127  
    1919libtransmission_a_SOURCES = \
    2020    announcer.c \
     21    announcer-http.c \
    2122    bandwidth.c \
    2223    bencode.c \
     
    7071noinst_HEADERS = \
    7172    announcer.h \
     73    announcer-common.h \
    7274    bandwidth.h \
    7375    bencode.h \
  • trunk/libtransmission/announcer.c

    r12120 r12127  
    1515
    1616#include <event2/buffer.h>
    17 #include <event2/event.h>
    18 #include <event2/http.h> /* for HTTP_OK */
     17#include <event2/event.h> /* evtimer */
     18
     19#define __LIBTRANSMISSION_ANNOUNCER_MODULE___
    1920
    2021#include "transmission.h"
    2122#include "announcer.h"
     23#include "announcer-common.h"
    2224#include "crypto.h"
    2325#include "net.h"
     
    3133#include "web.h"
    3234
    33 #define STARTED "started"
     35struct tr_tier;
     36
     37static void tier_build_log_name( const struct tr_tier * tier,
     38                                 char * buf, size_t buflen );
    3439
    3540#define dbgmsg( tier, ... ) \
    3641if( tr_deepLoggingIsActive( ) ) do { \
    3742  char name[128]; \
    38   tr_snprintf( name, sizeof( name ), "[%s--%s]", tr_torrentName( tier->tor ), \
    39       ( tier->currentTracker ? tier->currentTracker->hostname : "" ) ); \
     43  tier_build_log_name( tier, name, sizeof( name ) ); \
    4044  tr_deepLog( __FILE__, __LINE__, name, __VA_ARGS__ ); \
    4145} while( 0 )
     
    6266    /* this is an upper limit for the frequency of LDS announces */
    6367    LPD_HOUSEKEEPING_INTERVAL_SECS = 5
    64 
    6568};
    6669
     
    6972***/
    7073
    71 /**
    72  * Since we can't poll a tr_torrent's fields after it's destroyed,
    73  * we pre-build the "stop" announcement message when a torrent
    74  * is removed from Transmission
    75  */
    76 struct stop_message
    77 {
    78     char * url;
    79     uint64_t up;
    80     uint64_t down;
    81 };
    82 
    83 static void
    84 stopFree( struct stop_message * stop )
    85 {
    86     tr_free( stop->url );
    87     tr_free( stop );
    88 }
     74const char*
     75tr_announce_event_get_string( tr_announce_event e )
     76{
     77    switch( e )
     78    {
     79        case TR_ANNOUNCE_EVENT_COMPLETED:  return "completed";
     80        case TR_ANNOUNCE_EVENT_STARTED:    return "started";
     81        case TR_ANNOUNCE_EVENT_STOPPED:    return "stopped";
     82        default:                           return "";
     83    }
     84}
     85
     86/***
     87****
     88***/
    8989
    9090static int
     
    106106compareStops( const void * va, const void * vb )
    107107{
    108     const struct stop_message * a = va;
    109     const struct stop_message * b = vb;
     108    const tr_announce_request * a = va;
     109    const tr_announce_request * b = vb;
    110110    return compareTransfer( a->up, a->down, b->up, b->down);
    111111}
     
    120120typedef struct tr_announcer
    121121{
    122     tr_ptrArray stops; /* struct stop_message */
     122   /* Since we can't poll a tr_torrent's fields after it's destroyed,
     123    * we pre-build the "stop" tr_announce_request when a torrent is
     124    * removed from Transmission */
     125    tr_ptrArray stops;
     126
    123127    tr_session * session;
    124128    struct event * upkeepTimer;
     
    135139}
    136140
    137 /* format: hostname + ':' + port */
    138 static char *
    139 getHostName( const char * url )
    140 {
    141     int port = 0;
    142     char * host = NULL;
    143     char * ret;
    144     tr_urlParse( url, -1, NULL, &host, &port, NULL );
    145     ret = tr_strdup_printf( "%s:%d", ( host ? host : "invalid" ), port );
    146     tr_free( host );
    147     return ret;
    148 }
    149 
    150141static inline time_t
    151142calcRescheduleWithJitter( const int minPeriod )
    152143{
    153     const double jitterFac = 0.1;
     144    const double jitter = 0.1;
    154145
    155146    assert( minPeriod > 0 );
     
    157148    return tr_time()
    158149        + minPeriod
    159         + tr_cryptoWeakRandInt( (int) ( minPeriod * jitterFac ) + 1 );
     150        + tr_cryptoWeakRandInt( (int) ( minPeriod * jitter ) + 1 );
    160151}
    161152
     
    214205    char * scrape;
    215206
    216     char * tracker_id;
     207    char * tracker_id_str;
    217208
    218209    int seederCount;
     
    225216    uint32_t id;
    226217}
    227 tr_tracker_item;
    228 
    229 static tr_tracker_item*
    230 trackerNew( const char  * announce,
    231             const char  * scrape,
    232             uint32_t      id )
    233 {
    234     tr_tracker_item * tracker = tr_new0( tr_tracker_item, 1  );
     218tr_tracker;
     219
     220/* format: hostname + ':' + port */
     221static char *
     222getHostName( const char * url )
     223{
     224    int port = 0;
     225    char * host = NULL;
     226    char * ret;
     227    tr_urlParse( url, -1, NULL, &host, &port, NULL );
     228    ret = tr_strdup_printf( "%s:%d", ( host ? host : "invalid" ), port );
     229    tr_free( host );
     230    return ret;
     231}
     232
     233static tr_tracker*
     234trackerNew( const char * announce, const char * scrape, uint32_t id )
     235{
     236    tr_tracker * tracker = tr_new0( tr_tracker, 1  );
    235237    tracker->hostname = getHostName( announce );
    236238    tracker->announce = tr_strdup( announce );
     
    246248trackerFree( void * vtracker )
    247249{
    248     tr_tracker_item * tracker = vtracker;
    249 
    250     tr_free( tracker->tracker_id );
     250    tr_tracker * tracker = vtracker;
     251
     252    tr_free( tracker->tracker_id_str );
    251253    tr_free( tracker->scrape );
    252254    tr_free( tracker->announce );
     
    262264
    263265/** @brief A group of trackers in a single tier, as per the multitracker spec */
    264 typedef struct
     266typedef struct tr_tier
    265267{
    266268    /* number of up/down/corrupt bytes since the last time we sent an
     
    268270    uint64_t byteCounts[3];
    269271
    270     tr_ptrArray trackers; /* tr_tracker_item */
    271     tr_tracker_item * currentTracker;
     272    tr_ptrArray trackers; /* tr_tracker */
     273    tr_tracker * currentTracker;
    272274    int currentTrackerIndex;
    273275
     
    287289    tr_bool lastAnnounceTimedOut;
    288290
    289     tr_ptrArray announceEvents; /* const char* */
     291    tr_announce_event * announce_events;
     292    int announce_event_count;
     293    int announce_event_alloc;
    290294
    291295    /* unique lookup key */
     
    317321    t = tr_new0( tr_tier, 1 );
    318322    t->key = nextKey++;
    319     t->announceEvents = TR_PTR_ARRAY_INIT;
     323    t->announce_events = NULL;
     324    t->announce_event_count = 0;
     325    t->announce_event_alloc = 0;
    320326    t->trackers = TR_PTR_ARRAY_INIT;
    321327    t->currentTracker = NULL;
     
    335341    tr_tier * tier = vtier;
    336342    tr_ptrArrayDestruct( &tier->trackers, trackerFree );
    337     tr_ptrArrayDestruct( &tier->announceEvents, NULL );
     343    tr_free( tier->announce_events );
    338344    tr_free( tier );
     345}
     346
     347static void
     348tier_build_log_name( const tr_tier * tier, char * buf, size_t buflen )
     349{
     350    tr_snprintf( buf, buflen, "[%s---%s]",
     351       ( tier && tier->tor ) ? tr_torrentName( tier->tor ) : "?",
     352       ( tier && tier->currentTracker ) ? tier->currentTracker->hostname : "?" );
    339353}
    340354
     
    359373
    360374static void
    361 tierAddTracker( tr_tier      * tier,
    362                 const char   * announce,
    363                 const char   * scrape,
    364                 uint32_t       id )
    365 {
    366     tr_tracker_item * tracker = trackerNew( announce, scrape, id );
     375tierAddTracker( tr_tier    * tier,
     376                const char * announce,
     377                const char * scrape,
     378                uint32_t     id )
     379{
     380    tr_tracker * tracker = trackerNew( announce, scrape, id );
    367381
    368382    tr_ptrArrayAppend( &tier->trackers, tracker );
     
    407421
    408422static tr_tier*
    409 getTier( tr_announcer * announcer, int torrentId, int tierId )
     423getTier( tr_announcer * announcer, const uint8_t * info_hash, int tierId )
    410424{
    411425    tr_tier * tier = NULL;
    412426
    413     if( announcer )
    414     {
    415         tr_torrent * tor = tr_torrentFindFromId( announcer->session, torrentId );
     427    if( announcer != NULL )
     428    {
     429        tr_session * session = announcer->session;
     430        tr_torrent * tor = tr_torrentFindFromHash( session, info_hash );
    416431
    417432        if( tor && tor->tiers )
     
    436451***/
    437452
    438 static const tr_tracker_event emptyEvent = { 0, NULL, NULL, NULL, 0, 0 };
     453static const tr_tracker_event TRACKER_EVENT_INIT = { 0, 0, 0, 0, 0, 0 };
    439454
    440455static void
     
    444459    {
    445460        tr_torrent_tiers * tiers = tier->tor->tiers;
    446         tr_tracker_event event = emptyEvent;
     461        tr_tracker_event event = TRACKER_EVENT_INIT;
    447462        event.messageType = type;
    448463        event.text = msg;
    449         event.tracker = tier->currentTracker ? tier->currentTracker->announce : NULL;
     464        if( tier->currentTracker )
     465            event.tracker = tier->currentTracker->announce;
    450466
    451467        if( tiers->callback != NULL )
     
    466482}
    467483
     484static void
     485publishError( tr_tier * tier, const char * msg )
     486{
     487    publishMessage( tier, msg, TR_TRACKER_ERROR );
     488}
     489
    468490static int8_t
    469491getSeedProbability( int seeds, int leechers )
     
    482504                 const tr_pex * pex, int n )
    483505{
    484     tr_tracker_event e = emptyEvent;
     506    tr_tracker_event e = TRACKER_EVENT_INIT;
    485507
    486508    e.messageType = TR_TRACKER_PEERS;
     
    493515}
    494516
    495 static size_t
    496 publishPeersCompact( tr_tier * tier, int seeds, int leechers,
    497                      const void * compact, int compactLen )
    498 {
    499     size_t n = 0;
    500     tr_pex * pex = tr_peerMgrCompactToPex( compact, compactLen, NULL, 0, &n );
    501     publishPeersPex( tier, seeds, leechers, pex, n );
    502     dbgmsg( tier, "got IPv4 list of %zu peers", n );
    503     tr_free( pex );
    504     return n;
    505 }
    506 
    507 static size_t
    508 publishPeersCompact6( tr_tier * tier, int seeds, int leechers,
    509                       const void * compact, int compactLen )
    510 {
    511     size_t n = 0;
    512     tr_pex * pex = tr_peerMgrCompact6ToPex( compact, compactLen, NULL, 0, &n );
    513     dbgmsg( tier, "got IPv6 list of %zu peers", n );
    514     publishPeersPex( tier, seeds, leechers, pex, n );
    515     tr_free( pex );
    516     return n;
    517 }
    518 
    519 static size_t
    520 publishPeersDict( tr_tier * tier, int seeds, int leechers, tr_benc * peerList )
    521 {
    522     size_t i;
    523     size_t n;
    524     const size_t len = tr_bencListSize( peerList );
    525     tr_pex * pex = tr_new0( tr_pex, len );
    526 
    527     for( i=n=0; i<len; ++i )
    528     {
    529         int64_t port;
    530         const char * ip;
    531         tr_address addr;
    532         tr_benc * peer = tr_bencListChild( peerList, i );
    533 
    534         if( peer == NULL )
    535             continue;
    536         if( !tr_bencDictFindStr( peer, "ip", &ip ) )
    537             continue;
    538         if( tr_pton( ip, &addr ) == NULL )
    539             continue;
    540         if( !tr_bencDictFindInt( peer, "port", &port ) )
    541             continue;
    542         if( ( port < 0 ) || ( port > USHRT_MAX ) )
    543             continue;
    544         if( !tr_isValidPeerAddress( &addr, port ) )
    545             continue;
    546 
    547         pex[n].addr = addr;
    548         pex[n].port = htons( (uint16_t)port );
    549         ++n;
    550     }
    551 
    552     dbgmsg( tier, "got benc list of %zu peers", n );
    553     publishPeersPex( tier, seeds, leechers, pex, n );
    554     tr_free( pex );
    555     return n;
    556 }
    557 
    558 static char*
    559 createAnnounceURL( const tr_announcer  * announcer,
    560                    const tr_torrent    * torrent,
    561                    const tr_tier       * tier,
    562                    const char          * eventName )
    563 {
    564     const int isStopping = !strcmp( eventName, "stopped" );
    565     const int numwant = isStopping ? 0 : NUMWANT;
    566     const tr_tracker_item  * tracker = tier->currentTracker;
    567     const char * ann = tracker->announce;
    568     struct evbuffer * buf = evbuffer_new( );
    569     const char * str;
    570     const unsigned char * ipv6;
    571 
    572     evbuffer_expand( buf, 2048 );
    573 
    574     evbuffer_add_printf( buf, "%s"
    575                               "%c"
    576                               "info_hash=%s"
    577                               "&peer_id=%s"
    578                               "&port=%d"
    579                               "&uploaded=%" PRIu64
    580                               "&downloaded=%" PRIu64
    581                               "&left=%" PRIu64
    582                               "&numwant=%d"
    583                               "&key=%x"
    584                               "&compact=1"
    585                               "&supportcrypto=1",
    586                               ann,
    587                               strchr( ann, '?' ) ? '&' : '?',
    588                               torrent->info.hashEscaped,
    589                               torrent->peer_id,
    590                               (int)tr_sessionGetPublicPeerPort( announcer->session ),
    591                               tier->byteCounts[TR_ANN_UP],
    592                               tier->byteCounts[TR_ANN_DOWN],
    593                               tr_cpLeftUntilComplete( &torrent->completion ),
    594                               numwant,
    595                               announcer->key );
    596 
    597     if( announcer->session->encryptionMode == TR_ENCRYPTION_REQUIRED )
    598         evbuffer_add_printf( buf, "&requirecrypto=1" );
    599 
    600     if( tier->byteCounts[TR_ANN_CORRUPT] )
    601         evbuffer_add_printf( buf, "&corrupt=%" PRIu64, tier->byteCounts[TR_ANN_CORRUPT] );
    602 
    603     str = eventName;
    604     if( str && *str )
    605         evbuffer_add_printf( buf, "&event=%s", str );
    606 
    607     str = tracker->tracker_id;
    608     if( str && *str )
    609         evbuffer_add_printf( buf, "&trackerid=%s", str );
    610 
    611     /* There are two incompatible techniques for announcing an IPv6 address.
    612        BEP-7 suggests adding an "ipv6=" parameter to the announce URL,
    613        while OpenTracker requires that peers announce twice, once over IPv4
    614        and once over IPv6.
    615 
    616        To be safe, we should do both: add the "ipv6=" parameter and
    617        announce twice. At any rate, we're already computing our IPv6
    618        address (for the LTEP handshake), so this comes for free. */
    619 
    620     ipv6 = tr_globalIPv6( );
    621     if( ipv6 ) {
    622         char ipv6_readable[INET6_ADDRSTRLEN];
    623         inet_ntop( AF_INET6, ipv6, ipv6_readable, INET6_ADDRSTRLEN );
    624         evbuffer_add_printf( buf, "&ipv6=");
    625         tr_http_escape( buf, ipv6_readable, -1, TRUE );
    626     }
    627 
    628     return evbuffer_free_to_str( buf );
    629 }
    630 
    631 
    632517/***
    633518****
     
    635520
    636521static void
    637 addTorrentToTier( tr_torrent_tiers  * tiers,
    638                   tr_torrent        * tor )
     522addTorrentToTier( tr_torrent_tiers * tiers, tr_torrent * tor )
    639523{
    640524    int i, n;
     
    749633
    750634static void
    751 tierAddAnnounce( tr_tier * tier, const char * announceEvent, time_t announceAt )
    752 {
     635dbgmsg_tier_announce_queue( const tr_tier * tier )
     636{
     637    if( tr_deepLoggingIsActive( ) )
     638    {
     639        int i;
     640        char * str;
     641        char name[128];
     642        struct evbuffer * buf = evbuffer_new( );
     643
     644        tier_build_log_name( tier, name, sizeof( name ) );
     645        for( i=0; i<tier->announce_event_count; ++i )
     646        {
     647            const tr_announce_event e = tier->announce_events[i];
     648            const char * str = tr_announce_event_get_string( e );
     649            evbuffer_add_printf( buf, "[%d:%s]", i, str );
     650        }
     651        str = evbuffer_free_to_str( buf );
     652        tr_deepLog( __FILE__, __LINE__, name, "announce queue is %s", str );
     653        tr_free( str );
     654    }
     655}
     656
     657static void
     658tier_announce_remove_trailing( tr_tier * tier, tr_announce_event e )
     659{
     660    while( ( tier->announce_event_count > 0 ) && ( tier->announce_events[tier->announce_event_count-1] == e ) )
     661        --tier->announce_event_count;
     662}
     663
     664static void
     665tier_announce_event_push( tr_tier * tier, tr_announce_event e, time_t announceAt )
     666{
     667    int i;
     668
    753669    assert( tier != NULL );
    754     assert( announceEvent != NULL );
    755 
    756     tr_ptrArrayAppend( &tier->announceEvents, (void*)announceEvent );
     670
     671    dbgmsg_tier_announce_queue( tier );
     672    dbgmsg( tier, "appending \"%s\" to announce queue", tr_announce_event_get_string( e ) );
     673
     674    if( tier->announce_event_count > 0 )
     675    {
     676        /* special case #1: if we're adding a "stopped" event,
     677         * dump everything leading up to it except "completed" */
     678        if( e == TR_ANNOUNCE_EVENT_STOPPED ) {
     679            tr_bool has_completed = FALSE;
     680            const tr_announce_event c = TR_ANNOUNCE_EVENT_COMPLETED;
     681            for( i=0; !has_completed && i<tier->announce_event_count; ++i )
     682                has_completed = c == tier->announce_events[i];
     683            tier->announce_event_count = 0;
     684            if( has_completed )
     685                tier->announce_events[tier->announce_event_count++] = c;
     686        }
     687
     688        /* special case #2: dump all empty strings leading up to this event */
     689        tier_announce_remove_trailing( tier, TR_ANNOUNCE_EVENT_NONE );
     690
     691        /* special case #3: no consecutive duplicates */
     692        tier_announce_remove_trailing( tier, e );
     693    }
     694
     695    /* make room in the array for another event */
     696    if( tier->announce_event_alloc <= tier->announce_event_count ) {
     697        tier->announce_event_alloc += 4;
     698        tier->announce_events = tr_renew( tr_announce_event,
     699                                          tier->announce_events,
     700                                          tier->announce_event_alloc );
     701    }
     702
     703    /* add it */
     704    tier->announce_events[tier->announce_event_count++] = e;
    757705    tier->announceAt = announceAt;
    758706
    759     dbgmsg( tier, "appended event \"%s\"; announcing in %d seconds", announceEvent, (int)difftime(announceAt,time(NULL)) );
    760 }
    761 
    762 static void
    763 torrentAddAnnounce( tr_torrent * tor, const char * announceEvent, time_t announceAt )
     707    dbgmsg_tier_announce_queue( tier );
     708    dbgmsg( tier, "announcing in %d seconds", (int)difftime(announceAt,tr_time()) );
     709}
     710
     711static tr_bool
     712tier_announce_event_pull( tr_tier * tier, tr_announce_event * setme )
     713{
     714    const tr_bool success = tier->announce_event_count > 0;
     715
     716    if( success )
     717    {
     718        *setme = tier->announce_events[0];
     719
     720        tr_removeElementFromArray( tier->announce_events,
     721                                   0, sizeof( tr_announce_event ),
     722                                   tier->announce_event_count-- );
     723    }
     724
     725    return success;
     726}
     727
     728static void
     729torrentAddAnnounce( tr_torrent * tor, tr_announce_event e, time_t announceAt )
    764730{
    765731    int i;
     
    772738    n = tr_ptrArraySize( &tiers->tiers );
    773739    for( i=0; i<n; ++i )
    774         tierAddAnnounce( tr_ptrArrayNth( &tiers->tiers, i ), announceEvent, announceAt );
     740    {
     741        tr_tier * tier = tr_ptrArrayNth( &tiers->tiers, i );
     742        tier_announce_event_push( tier, e, announceAt );
     743    }
    775744}
    776745
     
    778747tr_announcerTorrentStarted( tr_torrent * tor )
    779748{
    780     torrentAddAnnounce( tor, STARTED, tr_time( ) );
     749    torrentAddAnnounce( tor, TR_ANNOUNCE_EVENT_STARTED, tr_time( ) );
    781750}
    782751void
    783752tr_announcerManualAnnounce( tr_torrent * tor )
    784753{
    785     torrentAddAnnounce( tor, "", tr_time( ) );
     754    torrentAddAnnounce( tor, TR_ANNOUNCE_EVENT_NONE, tr_time( ) );
    786755}
    787756void
    788757tr_announcerTorrentStopped( tr_torrent * tor )
    789758{
    790     torrentAddAnnounce( tor, "stopped", tr_time( ) );
     759    torrentAddAnnounce( tor, TR_ANNOUNCE_EVENT_STOPPED, tr_time( ) );
    791760}
    792761void
    793762tr_announcerTorrentCompleted( tr_torrent * tor )
    794763{
    795     torrentAddAnnounce( tor, "completed", tr_time( ) );
     764    torrentAddAnnounce( tor, TR_ANNOUNCE_EVENT_COMPLETED, tr_time( ) );
    796765}
    797766void
     
    826795****
    827796***/
     797
     798static tr_announce_request *
     799announce_request_new( const tr_announcer  * announcer,
     800                      const tr_torrent    * tor,
     801                      const tr_tier       * tier,
     802                      tr_announce_event     event )
     803{
     804    tr_announce_request * req = tr_new0( tr_announce_request, 1 );
     805    req->url = tr_strdup( tier->currentTracker->announce );
     806    req->tracker_id_str = tr_strdup( tier->currentTracker->tracker_id_str );
     807    memcpy( req->info_hash, tor->info.hash, SHA_DIGEST_LENGTH );
     808    memcpy( req->peer_id, tor->peer_id, PEER_ID_LEN );
     809    req->up = tier->byteCounts[TR_ANN_UP];
     810    req->down = tier->byteCounts[TR_ANN_DOWN];
     811    req->corrupt = tier->byteCounts[TR_ANN_CORRUPT];
     812    req->left = tr_cpLeftUntilComplete( &tor->completion ),
     813    req->event = event;
     814    req->numwant = event == TR_ANNOUNCE_EVENT_STOPPED ? 0 : NUMWANT;
     815    req->key = announcer->key;
     816    req->partial_seed = tr_cpGetStatus( &tor->completion ) == TR_PARTIAL_SEED;
     817    tier_build_log_name( tier, req->log_name, sizeof( req->log_name ) );
     818    return req;
     819}
    828820
    829821void
     
    839831        {
    840832            tr_tier * tier = tr_ptrArrayNth( &tor->tiers->tiers, i );
    841 
    842833            if( tier->isRunning )
    843834            {
    844                 struct stop_message * s = tr_new0( struct stop_message, 1 );
    845                 s->up = tier->byteCounts[TR_ANN_UP];
    846                 s->down = tier->byteCounts[TR_ANN_DOWN];
    847                 s->url = createAnnounceURL( announcer, tor, tier, "stopped" );
    848                 tr_ptrArrayInsertSorted( &announcer->stops, s, compareStops );
     835                const tr_announce_event e = TR_ANNOUNCE_EVENT_STOPPED;
     836                tr_announce_request * req = announce_request_new( announcer, tor, tier, e );
     837                tr_ptrArrayInsertSorted( &announcer->stops, req, compareStops );
    849838            }
    850839        }
     
    855844}
    856845
    857 /***
    858 ****
    859 ***/
    860 
    861 static tr_bool
    862 parseAnnounceResponse( tr_tier     * tier,
    863                        const char  * response,
    864                        size_t        responseLen,
    865                        tr_bool     * gotScrape )
    866 {
    867     tr_benc benc;
    868     tr_bool success = FALSE;
    869     int scrapeFields = 0;
    870     const int bencLoaded = !tr_bencLoad( response, responseLen, &benc, NULL );
    871 
    872     if( getenv( "TR_CURL_VERBOSE" ) != NULL )
    873     {
    874         char * str = tr_bencToStr( &benc, TR_FMT_JSON, NULL );
    875         fprintf( stderr, "Announce response:\n< %s\n", str );
    876         tr_free( str );
    877     }
    878 
    879     dbgmsg( tier, "response len: %d, isBenc: %d", (int)responseLen, (int)bencLoaded );
    880     publishErrorClear( tier );
    881     if( bencLoaded && tr_bencIsDict( &benc ) )
    882     {
    883         size_t rawlen;
    884         int64_t i;
    885         tr_benc * tmp;
    886         const char * str;
    887         const uint8_t * raw;
    888         tr_bool gotPeers = FALSE;
    889         int peerCount = 0;
    890 
    891         success = TRUE;
    892 
    893         if( tr_bencDictFindStr( &benc, "failure reason", &str ) )
    894         {
    895             tr_strlcpy( tier->lastAnnounceStr, str,
    896                         sizeof( tier->lastAnnounceStr ) );
    897             dbgmsg( tier, "tracker gave \"%s\"", str );
    898             publishMessage( tier, str, TR_TRACKER_ERROR );
    899             success = FALSE;
    900         }
    901 
    902         if( tr_bencDictFindStr( &benc, "warning message", &str ) )
    903         {
    904             tr_strlcpy( tier->lastAnnounceStr, str,
    905                         sizeof( tier->lastAnnounceStr ) );
    906             dbgmsg( tier, "tracker gave \"%s\"", str );
    907             publishWarning( tier, str );
    908         }
    909 
    910         if( tr_bencDictFindInt( &benc, "interval", &i ) )
    911         {
    912             dbgmsg( tier, "setting interval to %d", (int)i );
    913             tier->announceIntervalSec = i;
    914         }
    915 
    916         if( tr_bencDictFindInt( &benc, "min interval", &i ) )
    917         {
    918             dbgmsg( tier, "setting min interval to %d", (int)i );
    919             tier->announceMinIntervalSec = i;
    920         }
    921 
    922         if( tr_bencDictFindStr( &benc, "tracker id", &str ) )
    923         {
    924             tier->currentTracker->tracker_id = tr_strdup( str );
    925         }
    926 
    927         if( !tr_bencDictFindInt( &benc, "complete", &i ) )
    928             tier->currentTracker->seederCount = 0;
    929         else {
    930             ++scrapeFields;
    931             tier->currentTracker->seederCount = i;
    932         }
    933 
    934         if( !tr_bencDictFindInt( &benc, "incomplete", &i ) )
    935             tier->currentTracker->leecherCount = 0;
    936         else {
    937             ++scrapeFields;
    938             tier->currentTracker->leecherCount = i;
    939         }
    940 
    941         if( tr_bencDictFindInt( &benc, "downloaded", &i ) )
    942         {
    943             ++scrapeFields;
    944             tier->currentTracker->downloadCount = i;
    945         }
    946 
    947         if( tr_bencDictFindRaw( &benc, "peers", &raw, &rawlen ) )
    948         {
    949             /* "compact" extension */
    950             const int seeders = tier->currentTracker->seederCount;
    951             const int leechers = tier->currentTracker->leecherCount;
    952             peerCount += publishPeersCompact( tier, seeders, leechers, raw, rawlen );
    953             gotPeers = TRUE;
    954         }
    955         else if( tr_bencDictFindList( &benc, "peers", &tmp ) )
    956         {
    957             /* original version of peers */
    958             const int seeders = tier->currentTracker->seederCount;
    959             const int leechers = tier->currentTracker->leecherCount;
    960             peerCount += publishPeersDict( tier, seeders, leechers, tmp );
    961             gotPeers = TRUE;
    962         }
    963 
    964         if( tr_bencDictFindRaw( &benc, "peers6", &raw, &rawlen ) )
    965         {
    966             /* "compact" extension */
    967             const int seeders = tier->currentTracker->seederCount;
    968             const int leechers = tier->currentTracker->leecherCount;
    969             peerCount += publishPeersCompact6( tier, seeders, leechers, raw, rawlen );
    970             gotPeers = TRUE;
    971         }
    972 
    973         if( tier->lastAnnounceStr[0] == '\0' )
    974             tr_strlcpy( tier->lastAnnounceStr, _( "Success" ),
    975                         sizeof( tier->lastAnnounceStr ) );
    976 
    977         if( gotPeers )
    978             tier->lastAnnouncePeerCount = peerCount;
    979     }
    980 
    981     if( bencLoaded )
    982         tr_bencFree( &benc );
    983 
    984     *gotScrape = scrapeFields >= 2;
    985 
    986     return success;
    987 }
    988 
    989846static int
    990 getRetryInterval( const tr_tracker_item * t )
     847getRetryInterval( const tr_tracker * t )
    991848{
    992849    int minutes;
     
    1005862struct announce_data
    1006863{
    1007     int torrentId;
    1008864    int tierId;
    1009865    time_t timeSent;
    1010     const char * event;
     866    tr_announce_event event;
    1011867
    1012868    /** If the request succeeds, the value for tier's "isRunning" flag */
     
    1015871
    1016872static void
    1017 onAnnounceDone( tr_session   * session,
    1018                 tr_bool        didConnect,
    1019                 tr_bool        didTimeout,
    1020                 long           responseCode,
    1021                 const void   * response,
    1022                 size_t         responseLen,
    1023                 void         * vdata )
     873on_announce_error( tr_tier * tier, const char * err, tr_announce_event e )
     874{
     875    int interval;
     876
     877    /* set the error message */
     878    dbgmsg( tier, "%s", err );
     879    tr_torinf( tier->tor, "%s", err );
     880    tr_strlcpy( tier->lastAnnounceStr, err, sizeof( tier->lastAnnounceStr ) );
     881
     882    /* switch to the next tracker */
     883    tierIncrementTracker( tier );
     884
     885    /* schedule a reannounce */
     886    interval = getRetryInterval( tier->currentTracker );
     887    dbgmsg( tier, "Retrying announce in %d seconds.", interval );
     888    tier_announce_event_push( tier, e, tr_time( ) + interval );
     889}
     890
     891static void
     892on_announce_done( tr_session                  * session,
     893                  const tr_announce_response  * response,
     894                  void                        * vdata )
    1024895{
    1025896    tr_announcer * announcer = session->announcer;
    1026897    struct announce_data * data = vdata;
    1027     tr_tier * tier = getTier( announcer, data->torrentId, data->tierId );
     898    tr_tier * tier = getTier( announcer, response->info_hash, data->tierId );
    1028899    const time_t now = tr_time( );
    1029     const char * announceEvent = data->event;
    1030 
    1031     if( tier )
    1032     {
    1033         tr_tracker_item * tracker;
     900    const tr_announce_event event = data->event;
     901
     902    if( tier != NULL )
     903    {
     904        tr_tracker * tracker;
     905
     906        dbgmsg( tier, "Got announce response: "
     907                      "connected:%d "
     908                      "timeout:%d "
     909                      "seeders:%d "
     910                      "leechers:%d "
     911                      "downloads:%d "
     912                      "interval:%d "
     913                      "min_interval:%d "
     914                      "tracker_id_str:%s "
     915                      "pex:%zu "
     916                      "pex6:%zu "
     917                      "err:%s "
     918                      "warn:%s",
     919                      (int)response->did_connect,
     920                      (int)response->did_timeout,
     921                      response->seeders,
     922                      response->leechers,
     923                      response->downloads,
     924                      response->interval,
     925                      response->min_interval,
     926                      response->tracker_id_str ? response->tracker_id_str : "none",
     927                      response->pex_count,
     928                      response->pex6_count,
     929                      response->errmsg ? response->errmsg : "none",
     930                      response->warning ? response->warning : "none" );
    1034931
    1035932        tier->lastAnnounceTime = now;
    1036         tier->lastAnnounceTimedOut = didTimeout;
     933        tier->lastAnnounceTimedOut = response->did_timeout;
    1037934        tier->lastAnnounceSucceeded = FALSE;
    1038935        tier->isAnnouncing = FALSE;
     
    1042939            ++tracker->consecutiveAnnounceFailures;
    1043940
    1044         if( responseCode == HTTP_OK )
     941        if( !response->did_connect )
    1045942        {
    1046             tr_bool gotScrape;
    1047             const tr_bool isStopped = !strcmp( announceEvent, "stopped" );
    1048 
    1049             if( parseAnnounceResponse( tier, response, responseLen, &gotScrape ) )
     943            on_announce_error( tier, _( "Could not connect to tracker" ), event );
     944        }
     945        else if( response->did_timeout )
     946        {
     947            on_announce_error( tier, _( "Tracker did not respond" ), event );
     948        }
     949        else if( response->errmsg )
     950        {
     951            publishError( tier, response->errmsg );
     952            on_announce_error( tier, response->errmsg, event );
     953        }
     954        else
     955        {
     956            int i;
     957            const char * str;
     958            const tr_bool isStopped = event == TR_ANNOUNCE_EVENT_STOPPED;
     959
     960            publishErrorClear( tier );
     961
     962            if(( tracker = tier->currentTracker ))
     963                tracker->consecutiveAnnounceFailures = 0;
     964
     965            if(( str = response->warning ))
    1050966            {
    1051                 tier->lastAnnounceSucceeded = TRUE;
    1052                 tier->isRunning = data->isRunningOnSuccess;
    1053 
    1054                 if(( tracker = tier->currentTracker ))
    1055                 {
    1056                     tracker->consecutiveAnnounceFailures = 0;
    1057                 }
    1058 
    1059                 if( gotScrape )
    1060                 {
    1061                     tier->lastScrapeTime = now;
    1062                     tier->lastScrapeSucceeded = TRUE;
    1063                     tier->scrapeAt = now + tier->scrapeIntervalSec;
    1064                 }
     967                tr_strlcpy( tier->lastAnnounceStr, str, sizeof( tier->lastAnnounceStr ) );
     968                dbgmsg( tier, "tracker gave \"%s\"", str );
     969                publishWarning( tier, str );
    1065970            }
     971
     972            if(( i = response->min_interval ))
     973                tier->announceMinIntervalSec = i;
     974
     975            if(( i = response->interval ))
     976                tier->announceIntervalSec = i;
     977
     978            if(( str = response->tracker_id_str ))
     979            {
     980                tr_free( tier->currentTracker->tracker_id_str );
     981                tier->currentTracker->tracker_id_str = tr_strdup( str );
     982            }
     983
     984            tier->currentTracker->seederCount = response->seeders;
     985            tier->currentTracker->leecherCount = response->leechers;
     986            tier->currentTracker->downloadCount = response->downloads;
     987
     988            if( response->pex_count > 0 )
     989                publishPeersPex( tier, response->seeders, response->leechers,
     990                                 response->pex, response->pex_count );
     991
     992            if( response->pex6_count > 0 )
     993                publishPeersPex( tier, response->seeders, response->leechers,
     994                                 response->pex6, response->pex6_count );
     995
     996            if( !*tier->lastAnnounceStr )
     997                tr_strlcpy( tier->lastAnnounceStr, _( "Success" ),
     998                            sizeof( tier->lastAnnounceStr ) );
     999
     1000            tier->isRunning = data->isRunningOnSuccess;
     1001            tier->scrapeAt = now + tier->scrapeIntervalSec;
     1002            tier->lastScrapeTime = now;
     1003            tier->lastScrapeSucceeded = TRUE;
     1004            tier->lastAnnounceSucceeded = TRUE;
     1005            tier->lastAnnouncePeerCount = response->pex_count
     1006                                        + response->pex6_count;
    10661007
    10671008            if( isStopped )
     
    10751016            }
    10761017
    1077             if( !isStopped && !tr_ptrArraySize( &tier->announceEvents ) )
     1018            if( !isStopped && !tier->announce_event_count )
    10781019            {
    10791020                /* the queue is empty, so enqueue a perodic update */
    1080                 const int interval = tier->announceIntervalSec;
    1081                 dbgmsg( tier, "Sending periodic reannounce in %d seconds", interval );
    1082                 tierAddAnnounce( tier, "", now + interval );
     1021                i = tier->announceIntervalSec;
     1022                dbgmsg( tier, "Sending periodic reannounce in %d seconds", i );
     1023                tier_announce_event_push( tier, TR_ANNOUNCE_EVENT_NONE, now + i );
    10831024            }
    1084         }
    1085         else
    1086         {
    1087             int interval;
    1088 
    1089             if( !didConnect )
    1090                 tr_strlcpy( tier->lastAnnounceStr, _( "Could not connect to tracker" ),
    1091                             sizeof( tier->lastAnnounceStr ) );
    1092             else if( !responseCode )
    1093                 tr_strlcpy( tier->lastAnnounceStr, _( "Tracker did not respond" ),
    1094                             sizeof( tier->lastAnnounceStr ) );
    1095             else {
    1096                 /* %1$ld - http status code, such as 404
    1097                  * %2$s - human-readable explanation of the http status code */
    1098                 tr_snprintf( tier->lastAnnounceStr, sizeof( tier->lastAnnounceStr ),
    1099                              _( "Tracker gave HTTP response code %1$ld (%2$s)" ),
    1100                              responseCode,
    1101                              tr_webGetResponseStr( responseCode ) );
    1102                 if( responseCode >= 400 )
    1103                     if( tr_torrentIsPrivate( tier->tor ) || ( tier->tor->info.trackerCount == 1 ) )
    1104                         publishWarning( tier, tier->lastAnnounceStr );
    1105             }
    1106             dbgmsg( tier, "%s", tier->lastAnnounceStr );
    1107             tr_torinf( tier->tor, "%s", tier->lastAnnounceStr );
    1108 
    1109             tierIncrementTracker( tier );
    1110 
    1111             /* schedule the next announce */
    1112             interval = getRetryInterval( tier->currentTracker );
    1113             dbgmsg( tier, "Retrying announce in %d seconds.", interval );
    1114             tierAddAnnounce( tier, announceEvent, now + interval );
    11151025        }
    11161026    }
     
    11221032}
    11231033
    1124 static const char*
    1125 getNextAnnounceEvent( tr_tier * tier )
    1126 {
    1127     int i, n;
    1128     int pos = -1;
    1129     tr_ptrArray tmp;
    1130     const char ** events;
    1131     const char * str = NULL;
    1132 
    1133     assert( tier != NULL );
    1134     assert( tr_isTorrent( tier->tor ) );
    1135 
    1136     events = (const char**) tr_ptrArrayPeek( &tier->announceEvents, &n );
    1137 
    1138     /* special case #1: if "stopped" is in the queue,
    1139      * ignore everything before it except "completed" */
    1140     if( pos == -1 ) {
    1141         tr_bool completed = FALSE;
    1142         for( i = 0; i < n; ++i ) {
    1143             if( !strcmp( events[i], "completed" ) )
    1144                 completed = TRUE;
    1145             if( !strcmp( events[i], "stopped" ) )
    1146                 break;
    1147         }
    1148         if( !completed && ( i <  n ) )
    1149             pos = i;
    1150     }
    1151 
    1152     /* special case #2: don't use empty strings if something follows them */
    1153     if( pos == -1 ) {
    1154         for( i = 0; i < n; ++i )
    1155             if( *events[i] )
    1156                 break;
    1157         if( i < n )
    1158             pos = i;
    1159     }
    1160 
    1161     /* default: use the next in the queue */
    1162     if( ( pos == -1 ) && ( n > 0 ) )
    1163         pos = 0;
    1164 
    1165     /* special case #3: if there are duplicate requests in a row, skip to the last one */
    1166     if( pos >= 0 ) {
    1167         for( i=pos+1; i<n; ++i )
    1168             if( strcmp( events[pos], events[i] ) )
    1169                 break;
    1170         pos = i - 1;
    1171     }
    1172 
    1173     /* special case #4: BEP 21: "In order to tell the tracker that a peer is a
    1174      * partial seed, it MUST send an event=paused parameter in every
    1175      * announce while it is a partial seed." */
    1176     str = pos>=0 ? events[pos] : NULL;
    1177     if( tr_cpGetStatus( &tier->tor->completion ) == TR_PARTIAL_SEED )
    1178         if( !str || strcmp( str, "stopped" ) )
    1179             str = "paused";
    1180 
    1181 #if 0
    1182 for( i=0; i<n; ++i ) fprintf( stderr, "(%d)\"%s\" ", i, events[i] );
    1183 fprintf( stderr, "\n" );
    1184 fprintf( stderr, "using (%d)\"%s\"\n", pos, events[pos] );
    1185 if( strcmp( events[pos], str ) ) fprintf( stderr, "...but really using [%s]\n", str );
    1186 #endif
    1187 
    1188     /* announceEvents array upkeep */
    1189     tmp = TR_PTR_ARRAY_INIT;
    1190     for( i=pos+1; i<n; ++i )
    1191         tr_ptrArrayAppend( &tmp, (void*)events[i] );
    1192     tr_ptrArrayDestruct( &tier->announceEvents, NULL );
    1193     tier->announceEvents = tmp;
    1194 
    1195     return str;
     1034static void
     1035announce_request_delegate( tr_announcer               * announcer,
     1036                           tr_announce_request        * request,
     1037                           tr_announce_response_func  * callback,
     1038                           void                       * callback_data )
     1039{
     1040    tr_session * session = announcer->session;
     1041
     1042    if( strstr( request->url, "http://" ) )
     1043        tr_tracker_http_announce( session, request, callback, callback_data );
     1044    else
     1045        fprintf( stderr, "can't handle [%s] yet\n", request->url );
     1046
     1047    tr_free( request->tracker_id_str );
     1048    tr_free( request->url );
     1049    tr_free( request );
    11961050}
    11971051
     
    11991053tierAnnounce( tr_announcer * announcer, tr_tier * tier )
    12001054{
    1201     const char * announceEvent = getNextAnnounceEvent( tier );
     1055    tr_announce_event announce_event;
    12021056
    12031057    assert( !tier->isAnnouncing );
    12041058
    1205     if( announceEvent != NULL )
    1206     {
    1207         char * url;
     1059    if( tier_announce_event_pull( tier, &announce_event ) )
     1060    {
    12081061        struct announce_data * data;
    12091062        const tr_torrent * tor = tier->tor;
    12101063        const time_t now = tr_time( );
    12111064
     1065        tr_announce_request * req = announce_request_new( announcer, tor, tier, announce_event );
     1066
    12121067        data = tr_new0( struct announce_data, 1 );
    1213         data->torrentId = tr_torrentId( tor );
    12141068        data->tierId = tier->key;
    12151069        data->isRunningOnSuccess = tor->isRunning;
    12161070        data->timeSent = now;
    1217         data->event = announceEvent;
    1218         url = createAnnounceURL( announcer, tor, tier, data->event );
     1071        data->event = announce_event;
    12191072
    12201073        tier->isAnnouncing = TRUE;
    12211074        tier->lastAnnounceStartTime = now;
    12221075        --announcer->slotsAvailable;
    1223         tr_webRun( announcer->session, url, NULL, onAnnounceDone, data );
    1224 
    1225         tr_free( url );
     1076
     1077        announce_request_delegate( announcer, req, on_announce_done, data );
    12261078    }
    12271079}
     
    12291081/***
    12301082****
     1083****  SCRAPE
     1084****
    12311085***/
    12321086
    1233 static tr_bool
    1234 parseScrapeResponse( tr_tier     * tier,
    1235                      const char  * response,
    1236                      size_t        responseLen,
    1237                      char        * result,
    1238                      size_t        resultlen )
    1239 {
    1240     tr_bool success = FALSE;
    1241     tr_benc benc, *files;
    1242     const int bencLoaded = !tr_bencLoad( response, responseLen, &benc, NULL );
    1243     if( bencLoaded && tr_bencDictFindDict( &benc, "files", &files ) )
    1244     {
    1245         const char * key;
    1246         tr_benc * val;
    1247         int i = 0;
    1248         while( tr_bencDictChild( files, i++, &key, &val ))
     1087static void
     1088on_scrape_error( tr_tier * tier, const char * errmsg )
     1089{
     1090    int interval;
     1091
     1092    /* set the error message */
     1093    dbgmsg( tier, "Scrape error: %s", errmsg );
     1094    tr_torinf( tier->tor, "Scrape error: %s", errmsg );
     1095    tr_strlcpy( tier->lastScrapeStr, errmsg, sizeof( tier->lastScrapeStr ) );
     1096
     1097    /* switch to the next tracker */
     1098    tierIncrementTracker( tier );
     1099
     1100    /* schedule a rescrape */
     1101    interval = getRetryInterval( tier->currentTracker );
     1102    dbgmsg( tier, "Retrying scrape in %d seconds.", interval );
     1103    tier->lastScrapeSucceeded = FALSE;
     1104    tier->scrapeAt = tr_time() + interval;
     1105}
     1106
     1107static tr_tier *
     1108find_tier( tr_torrent * tor, const char * url )
     1109{
     1110    int i;
     1111    int n = tr_ptrArraySize( &tor->tiers->tiers );
     1112    tr_tier ** tiers = (tr_tier**) tr_ptrArrayBase( &tor->tiers->tiers );
     1113   
     1114    for( i=0; i<n; ++i ) {
     1115        tr_tracker * tracker = tiers[i]->currentTracker;
     1116        if( tracker && !tr_strcmp0( tracker->scrape, url ) )
     1117            return tiers[i];
     1118    }
     1119
     1120    return NULL;
     1121}
     1122
     1123static void
     1124on_scrape_done( tr_session                * session,
     1125                const tr_scrape_response  * response,
     1126                void                      * user_data UNUSED )
     1127{
     1128    int i;
     1129    const time_t now = tr_time( );
     1130    tr_announcer * announcer = session->announcer;
     1131
     1132    for( i=0; i<response->row_count; ++i )
     1133    {
     1134        const struct tr_scrape_response_row * row = &response->rows[i];
     1135        tr_torrent * tor = tr_torrentFindFromHash( session, row->info_hash );
     1136
     1137        if( tor != NULL )
    12491138        {
    1250             int64_t intVal;
    1251             tr_benc * flags;
    1252 
    1253             if( memcmp( tier->tor->info.hash, key, SHA_DIGEST_LENGTH ) )
    1254                 continue;
    1255 
    1256             success = TRUE;
    1257             publishErrorClear( tier );
    1258 
    1259             if( ( tr_bencDictFindInt( val, "complete", &intVal ) ) )
    1260                 tier->currentTracker->seederCount = intVal;
    1261 
    1262             if( ( tr_bencDictFindInt( val, "incomplete", &intVal ) ) )
    1263                 tier->currentTracker->leecherCount = intVal;
    1264 
    1265             if( ( tr_bencDictFindInt( val, "downloaded", &intVal ) ) )
    1266                 tier->currentTracker->downloadCount = intVal;
    1267 
    1268             if( ( tr_bencDictFindInt( val, "downloaders", &intVal ) ) )
    1269                 tier->currentTracker->downloaderCount = intVal;
    1270 
    1271             if( tr_bencDictFindDict( val, "flags", &flags ) )
    1272                 if( ( tr_bencDictFindInt( flags, "min_request_interval", &intVal ) ) )
    1273                     tier->scrapeIntervalSec = MAX( DEFAULT_SCRAPE_INTERVAL_SEC, (int)intVal );
    1274 
    1275             tr_tordbg( tier->tor,
    1276                        "Scrape successful. Rescraping in %d seconds.",
    1277                        tier->scrapeIntervalSec );
    1278         }
    1279     }
    1280 
    1281     if( bencLoaded )
    1282         tr_bencFree( &benc );
    1283 
    1284     if( success )
    1285         tr_strlcpy( result, _( "Success" ), resultlen );
    1286     else
    1287         tr_strlcpy( result, _( "Error parsing response" ), resultlen );
    1288 
    1289     return success;
    1290 }
    1291 
    1292 static void
    1293 onScrapeDone( tr_session   * session,
    1294               tr_bool        didConnect,
    1295               tr_bool        didTimeout,
    1296               long           responseCode,
    1297               const void   * response,
    1298               size_t         responseLen,
    1299               void         * vdata )
    1300 {
    1301     tr_bool success = FALSE;
    1302     tr_announcer * announcer = session->announcer;
    1303     struct announce_data * data = vdata;
    1304     tr_tier * tier = getTier( announcer, data->torrentId, data->tierId );
    1305     const time_t now = tr_time( );
     1139            tr_tier * tier = find_tier( tor, response->url );
     1140
     1141            if( tier != NULL )
     1142            {
     1143                dbgmsg( tier, "scraped url:%s -- "
     1144                              "did_connect:%d "
     1145                              "did_timeout:%d "
     1146                              "seeders:%d "
     1147                              "leechers:%d "
     1148                              "downloads:%d "
     1149                              "downloaders:%d "
     1150                              "min_request_interval:%d "
     1151                              "err:%s ",
     1152                              response->url,
     1153                              (int)response->did_connect,
     1154                              (int)response->did_timeout,
     1155                              row->seeders,
     1156                              row->leechers,
     1157                              row->downloads,
     1158                              row->downloaders,
     1159                              response->min_request_interval,
     1160                              response->errmsg ? response->errmsg : "none" );
     1161
     1162                tier->isScraping = FALSE;
     1163                tier->lastScrapeTime = now;
     1164                tier->lastScrapeSucceeded = FALSE;
     1165                tier->lastScrapeTimedOut = response->did_timeout;
     1166
     1167                if( !response->did_connect )
     1168                {
     1169                    on_scrape_error( tier, _( "Could not connect to tracker" ) );
     1170                }
     1171                else if( response->did_timeout )
     1172                {
     1173                    on_scrape_error( tier, _( "Tracker did not respond" ) );
     1174                }
     1175                else if( response->errmsg )
     1176                {
     1177                    on_scrape_error( tier, response->errmsg );
     1178                }
     1179                else
     1180                {
     1181                    tr_tracker * tracker;
     1182
     1183                    tier->lastScrapeSucceeded = TRUE;
     1184                    tier->scrapeIntervalSec = MAX( DEFAULT_SCRAPE_INTERVAL_SEC,
     1185                                                   response->min_request_interval );
     1186                    tier->scrapeAt = now + tier->scrapeIntervalSec;
     1187                    tr_tordbg( tier->tor, "Scrape successful. Rescraping in %d seconds.",
     1188                               tier->scrapeIntervalSec );
     1189
     1190                    if(( tracker = tier->currentTracker ))
     1191                    {
     1192                        tracker->seederCount = row->seeders;
     1193                        tracker->leecherCount = row->leechers;
     1194                        tracker->downloadCount = row->downloads;
     1195                        tracker->downloaderCount = row->downloaders;
     1196                    }
     1197                }
     1198            }
     1199        }
     1200    }
    13061201
    13071202    if( announcer )
    13081203        ++announcer->slotsAvailable;
    1309 
    1310     if( announcer && tier )
    1311     {
    1312         tier->isScraping = FALSE;
    1313         tier->lastScrapeTime = now;
    1314 
    1315         if( 200 <= responseCode && responseCode <= 299 )
    1316         {
    1317             const int interval = tier->scrapeIntervalSec;
    1318             tier->scrapeAt = now + interval;
    1319 
    1320             if( responseCode == HTTP_OK )
    1321                 success = parseScrapeResponse( tier, response, responseLen,
    1322                                                tier->lastScrapeStr, sizeof( tier->lastScrapeStr ) );
    1323             else
    1324                 tr_snprintf( tier->lastScrapeStr, sizeof( tier->lastScrapeStr ),
    1325                              _( "tracker gave HTTP Response Code %1$ld (%2$s)" ),
    1326                              responseCode, tr_webGetResponseStr( responseCode ) );
    1327             dbgmsg( tier, "%s", tier->lastScrapeStr );
    1328         }
    1329         else if( 300 <= responseCode && responseCode <= 399 )
    1330         {
    1331             /* this shouldn't happen; libcurl should handle this */
    1332             const int interval = 5;
    1333             tier->scrapeAt = now + interval;
    1334             tr_snprintf( tier->lastScrapeStr, sizeof( tier->lastScrapeStr ),
    1335                          "Got a redirect. Retrying in %d seconds", interval );
    1336             dbgmsg( tier, "%s", tier->lastScrapeStr );
    1337         }
    1338         else
    1339         {
    1340             const int interval = getRetryInterval( tier->currentTracker );
    1341 
    1342             /* Don't retry on a 4xx.
    1343              * Retry at growing intervals on a 5xx */
    1344             if( 400 <= responseCode && responseCode <= 499 )
    1345                 tier->scrapeAt = 0;
    1346             else
    1347                 tier->scrapeAt = now + interval;
    1348 
    1349             /* %1$ld - http status code, such as 404
    1350              * %2$s - human-readable explanation of the http status code */
    1351             if( !didConnect )
    1352                 tr_strlcpy( tier->lastScrapeStr, _( "Could not connect to tracker" ),
    1353                             sizeof( tier->lastScrapeStr ) );
    1354             else if( !responseCode )
    1355                 tr_strlcpy( tier->lastScrapeStr, _( "tracker did not respond" ),
    1356                             sizeof( tier->lastScrapeStr ) );
    1357             else
    1358                 tr_snprintf( tier->lastScrapeStr, sizeof( tier->lastScrapeStr ),
    1359                              _( "tracker gave HTTP Response Code %1$ld (%2$s)" ),
    1360                              responseCode, tr_webGetResponseStr( responseCode ) );
    1361         }
    1362 
    1363         tier->lastScrapeSucceeded = success;
    1364         tier->lastScrapeTimedOut = didTimeout;
    1365     }
    1366 
    1367     tr_free( data );
     1204}
     1205
     1206static void
     1207scrape_request_delegate( tr_announcer             * announcer,
     1208                         tr_scrape_request        * request,
     1209                         tr_scrape_response_func  * callback,
     1210                         void                     * callback_data )
     1211{
     1212    tr_session * session = announcer->session;
     1213
     1214    if( strstr( request->url, "http://" ) )
     1215        tr_tracker_http_scrape( session, request, callback, callback_data );
     1216    else
     1217        fprintf( stderr, "can't handle [%s] yet\n", request->url );
    13681218}
    13691219
     
    13711221tierScrape( tr_announcer * announcer, tr_tier * tier )
    13721222{
    1373     char * url;
    1374     const char * scrape;
    1375     struct announce_data * data;
    1376 
     1223    tr_scrape_request request;
     1224
     1225    /* sanity clause */
    13771226    assert( tier );
    13781227    assert( !tier->isScraping );
     
    13801229    assert( tr_isTorrent( tier->tor ) );
    13811230
    1382     data = tr_new0( struct announce_data, 1 );
    1383     data->torrentId = tr_torrentId( tier->tor );
    1384     data->tierId = tier->key;
    1385 
    1386     scrape = tier->currentTracker->scrape;
    1387 
    1388     url = tr_strdup_printf( "%s%cinfo_hash=%s",
    1389                             scrape,
    1390                             strchr( scrape, '?' ) ? '&' : '?',
    1391                             tier->tor->info.hashEscaped );
    1392 
     1231    /* initialize the request */
     1232    request.url = tier->currentTracker->scrape;
     1233    request.info_hash_count = 1;
     1234    memcpy( request.info_hash[0], tier->tor->info.hash, SHA_DIGEST_LENGTH );
     1235    tier_build_log_name( tier, request.log_name, sizeof( request.log_name ) );
     1236
     1237    /* start scraping */
     1238    dbgmsg( tier, "scraping \"%s\"", request.url );
    13931239    tier->isScraping = TRUE;
    13941240    tier->lastScrapeStartTime = tr_time( );
    13951241    --announcer->slotsAvailable;
    1396     dbgmsg( tier, "scraping \"%s\"", url );
    1397     tr_webRun( announcer->session, url, NULL, onScrapeDone, data );
    1398 
    1399     tr_free( url );
     1242    scrape_request_delegate( announcer, &request, on_scrape_done, NULL );
    14001243}
    14011244
     
    14071250
    14081251    for( i=0; i<n; ++i )
    1409     {
    1410         struct stop_message * stop = tr_ptrArrayNth( &announcer->stops, i );
    1411         tr_webRun( announcer->session, stop->url, NULL, NULL, NULL );
    1412         stopFree( stop );
    1413     }
     1252        announce_request_delegate( announcer, tr_ptrArrayNth( &announcer->stops, i ), NULL, NULL );
    14141253
    14151254    tr_ptrArrayClear( &announcer->stops );
     
    14231262        && ( tier->announceAt != 0 )
    14241263        && ( tier->announceAt <= now )
    1425         && ( tr_ptrArraySize( &tier->announceEvents ) != 0 );
     1264        && ( tier->announce_event_count > 0 );
    14261265}
    14271266
     
    14951334
    14961335        /* scrape some */
     1336        /* FIXME: multiscrape */
    14971337        n = MIN( tr_ptrArraySize( &scrapeMe ), announcer->slotsAvailable );
    14981338        for( i=0; i<n; ++i ) {
     
    15901430
    15911431    /* count the trackers... */
    1592     for( i=n=0, tierCount=tr_ptrArraySize( &torrent->tiers->tiers ); i<tierCount; ++i ) {
     1432    tierCount = tr_ptrArraySize( &torrent->tiers->tiers );
     1433    for( i=n=0; i<tierCount; ++i ) {
    15931434        const tr_tier * tier = tr_ptrArrayNth( &torrent->tiers->tiers, i );
    15941435        n += tr_ptrArraySize( &tier->trackers );
     
    16001441
    16011442    /* populate the stats */
    1602     for( i=0, tierCount=tr_ptrArraySize( &torrent->tiers->tiers ); i<tierCount; ++i )
     1443    tierCount = tr_ptrArraySize( &torrent->tiers->tiers );
     1444    for( i=0; i<tierCount; ++i )
    16031445    {
    16041446        int j;
     
    16071449        for( j=0; j<n; ++j )
    16081450        {
    1609             const tr_tracker_item * tracker = tr_ptrArrayNth( (tr_ptrArray*)&tier->trackers, j );
     1451            const tr_tracker * tracker = tr_ptrArrayNth( (tr_ptrArray*)&tier->trackers, j );
    16101452            tr_tracker_stat * st = ret + out++;
    16111453
     
    16931535
    16941536static void
    1695 trackerItemCopyAttributes( tr_tracker_item * t, const tr_tracker_item * o )
     1537trackerItemCopyAttributes( tr_tracker * t, const tr_tracker * o )
    16961538{
    16971539    assert( t != o );
     
    17081550tierCopyAttributes( tr_tier * t, const tr_tier * o )
    17091551{
    1710     int i, n;
    17111552    tr_tier bak;
    17121553
     
    17191560    t->tor = bak.tor;
    17201561    t->trackers = bak.trackers;
    1721     t->announceEvents = bak.announceEvents;
     1562    t->announce_events = tr_memdup( o->announce_events, sizeof( tr_announce_event ) * o->announce_event_count );
     1563    t->announce_event_count = o->announce_event_count;
     1564    t->announce_event_alloc = o->announce_event_count;
    17221565    t->currentTracker = bak.currentTracker;
    17231566    t->currentTrackerIndex = bak.currentTrackerIndex;
    1724 
    1725     tr_ptrArrayClear( &t->announceEvents );
    1726     for( i=0, n=tr_ptrArraySize(&o->announceEvents); i<n; ++i )
    1727         tr_ptrArrayAppend( &t->announceEvents, tr_ptrArrayNth((tr_ptrArray*)&o->announceEvents,i) );
    17281567}
    17291568
     
    17621601                for( k=0, kn=tr_ptrArraySize(&t->trackers); k<kn; ++k )
    17631602                {
    1764                     tr_tracker_item * item = tr_ptrArrayNth(&t->trackers,k);
     1603                    tr_tracker * item = tr_ptrArrayNth(&t->trackers,k);
    17651604                    if( strcmp( o->currentTracker->announce, item->announce ) )
    17661605                        continue;
     
    17881627            tr_tier * tier = tiers[i];
    17891628            if( !tier->wasCopied )
    1790                 tierAddAnnounce( tier, STARTED, now );
     1629                tier_announce_event_push( tier, TR_ANNOUNCE_EVENT_STARTED, now );
    17911630        }
    17921631    }
  • trunk/libtransmission/metainfo.c

    r11709 r12127  
    384384}
    385385
    386 static int
    387 is_rfc2396_alnum( char ch )
    388 {
    389     return ( '0' <= ch && ch <= '9' )
    390         || ( 'A' <= ch && ch <= 'Z' )
    391         || ( 'a' <= ch && ch <= 'z' )
    392         || ch == '.'
    393         || ch == '-'
    394         || ch == '_'
    395         || ch == '~';
    396 }
    397 
    398 static void
    399 escape( char * out, const uint8_t * in, size_t in_len ) /* rfc2396 */
    400 {
    401     const uint8_t *end = in + in_len;
    402 
    403     while( in != end )
    404         if( is_rfc2396_alnum( *in ) )
    405             *out++ = (char) *in++;
    406         else
    407             out += tr_snprintf( out, 4, "%%%02x", (unsigned int)*in++ );
    408 
    409     *out = '\0';
    410 }
    411 
    412386static const char*
    413387tr_metainfoParseImpl( const tr_session  * session,
     
    447421            memcpy( inf->hash, raw, raw_len );
    448422            tr_sha1_to_hex( inf->hashString, inf->hash );
    449             escape( inf->hashEscaped, inf->hash, SHA_DIGEST_LENGTH );
    450423
    451424            /* maybe get the display name */
     
    469442        tr_sha1( inf->hash, bstr, len, NULL );
    470443        tr_sha1_to_hex( inf->hashString, inf->hash );
    471         escape( inf->hashEscaped, inf->hash, SHA_DIGEST_LENGTH );
    472444
    473445        if( infoDictLength != NULL )
  • trunk/libtransmission/transmission.h

    r12086 r12127  
    16781678    char               hashString[2 * SHA_DIGEST_LENGTH + 1];
    16791679
    1680     /* hash, escaped as per rfc2396 for tracker announces */
    1681     char               hashEscaped[3 * SHA_DIGEST_LENGTH + 1];
    1682 
    16831680    /* Flags */
    16841681    tr_bool            isPrivate;
  • trunk/libtransmission/web.c

    r12001 r12127  
    510510    return ret;
    511511}
     512
     513static int
     514is_rfc2396_alnum( uint8_t ch )
     515{
     516    return ( '0' <= ch && ch <= '9' )
     517        || ( 'A' <= ch && ch <= 'Z' )
     518        || ( 'a' <= ch && ch <= 'z' )
     519        || ch == '.'
     520        || ch == '-'
     521        || ch == '_'
     522        || ch == '~';
     523}
     524
     525void
     526tr_http_escape_sha1( char * out, const uint8_t * sha1_digest )
     527{
     528    const uint8_t * in = sha1_digest;
     529    const uint8_t * end = in + SHA_DIGEST_LENGTH;
     530
     531    while( in != end )
     532        if( is_rfc2396_alnum( *in ) )
     533            *out++ = (char) *in++;
     534        else
     535            out += tr_snprintf( out, 4, "%%%02x", (unsigned int)*in++ );
     536
     537    *out = '\0';
     538}
  • trunk/libtransmission/web.h

    r11896 r12127  
    7070void tr_http_escape( struct evbuffer *out, const char *str, int len, tr_bool escape_slashes );
    7171
     72void tr_http_escape_sha1( char * out, const uint8_t * sha1_digest );
     73
    7274char* tr_http_unescape( const char * str, int len );
    7375
  • trunk/utils/show.c

    r11709 r12127  
    198198        const char * scrape = inf->trackers[i].scrape;
    199199        char * url;
     200        char escaped[SHA_DIGEST_LENGTH*3 + 1];
    200201
    201202        if( scrape == NULL )
    202203            continue;
     204
     205        tr_http_escape_sha1( escaped, inf->hash );
    203206
    204207        url = tr_strdup_printf( "%s%cinfo_hash=%s",
    205208                                scrape,
    206209                                strchr( scrape, '?' ) ? '&' : '?',
    207                                 inf->hashEscaped );
     210                                escaped );
    208211
    209212        printf( "%s ... ", url );
Note: See TracChangeset for help on using the changeset viewer.