Changeset 4037


Ignore:
Timestamp:
Dec 2, 2007, 1:27:14 AM (14 years ago)
Author:
charles
Message:

backport a slew of bugfixes into the 0.9x branch, for an upcoming 0.95 release

Location:
branches/0.9x
Files:
26 edited

Legend:

Unmodified
Added
Removed
  • branches/0.9x/NEWS

    r3964 r4037  
    11NEWS file for Transmission <http://transmission.m0k.org/>
     2
     30.95 (2007/12/xx)
     4- All Platforms:
     5   + Fix port mapping bug that bit both Leopard and Linux
     6   + Fix bug that let speed-limited torrents upload too quickly
     7   + Fix router slowdown caused by sending too many tracker requests at once
     8   + Faster average upload speeds
     9   + Faster connection to peers after starting a torrent
     10   + Fix memory corruption error
     11   + Disable SWIFT for ratio-based trackers
     12- GTK:
     13   + Fix Nokia 770 crash
    214
    3150.94 (2007/11/25)
     
    2941   + Leopard: Fix bug with typing values in Inspector->Options
    3042   + Leopard: Fix bug with toggling Minimal View
    31 - GTK
     43- GTK:
    3244   + Better support for large files on some Linux systems
    3345   + Fix localization error in torrent inspector's dates
     
    4961
    50620.90 (2007/10/23)
    51  - Encryption support, with option to ignore unencrypted peers
    52  - Ban peers that send too much bad data
    53  - Only report downloaded, verified good pieces in tracker `download' field
    54  - Improved compliance with BitTorrent spec
    55  - MSE Tracker Extension support
    56  - Significant rewrite of the libtransmission back-end
    57  - Icon enhanced to support size of 512 x 512
    58  - OS X:
     63- All Platforms:
     64   + Encryption support, with option to ignore unencrypted peers
     65   + Only report downloaded, verified good pieces in tracker `download' field
     66   + Improved compliance with BitTorrent spec
     67   + MSE Tracker Extension support
     68   + Significant rewrite of the libtransmission back-end
     69- OS X:
    5970   + Per-torrent action menu
    6071   + Redesigned inspector with additional statistics and ability to be resized vertically in Peers and Files tabs
     
    6273   + Optimizations to decrease memory usage
    6374   + Sort and filter by tracker
     75   + Icon enhanced to support size of 512 x 512
    6476   + Various smaller interface additions and improvements
    65  - GTK:
     77- GTK:
    6678   + Various interface improvements
    6779   + Better compliance with the Gnome interface guidelines
    6880
    69810.82 (2007/09/09)
    70  - Fixed bug that could limit transfer speeds
    71  - Fixed bug that corrupted torrents > 4 GB
    72  - Fixed bug that could allow bad peers to send too many pieces
    73  - For peers supporting both Azureus's and LibTorrent's extensions, allow negotiation to decide which to use
    74  - Other minor fixes
     82- All Platforms:
     83   + Fixed bug that could limit transfer speeds
     84   + Fixed bug that corrupted torrents > 4 GB
     85   + Fixed bug that could allow bad peers to send too many pieces
     86   + For peers supporting both Azureus' and LibTorrent's extensions, allow negotiation to decide which to use
     87   +  Other minor fixes
    7588
    76890.81 (2007/08/22)
    77  - Fix 0.80 assertion crashes
    78  - Fix a bug that miscounted how many peers Transmission wants connected
    79  - Correct incorrect error messages
    80  - Memory leaks fixed
    81  - OS X:
     90- All Platforms:
     91   + Fix 0.80 assertion crashes
     92   + Fix a bug that miscounted how many peers Transmission wants connected
     93   + Clarify misleading error messages
     94   + Fix memory leaks
     95- OS X:
    8296   + Multiple fixes to creating torrents and adding new torrents
    8397   + Updated Russian and Spanish translations
    84  - GTK:
     98- GTK:
    8599   + Updated Dutch, Portuguese, French, and Slovakian translations
    86  - CLI:
     100- CLI:
    87101   + Better support for cli-driven torrent creation
    88102   + Fix a bug that misparsed command-line arguments
  • branches/0.9x/gtk/conf.c

    r3499 r4037  
    3939
    4040#include <libtransmission/transmission.h>
     41#include <libtransmission/bencode.h>
    4142
    4243#include "conf.h"
     
    250251    g_free( filename );
    251252}
     253
     254/***
     255****
     256***/
     257
     258#if !GLIB_CHECK_VERSION(2,8,0)
     259static void
     260tr_file_set_contents( const char * filename, const void * out, size_t len, GError* unused UNUSED )
     261{
     262    FILE * fp = fopen( filename, "wb+" );
     263    if( fp != NULL ) {
     264        fwrite( out, 1, len, fp );
     265        fclose( fp );
     266    }
     267}
     268#define g_file_set_contents tr_file_set_contents
     269#endif
     270
     271static char*
     272getCompat08PrefsFilename( void )
     273{
     274    assert( gl_confdir != NULL );
     275    return g_build_filename( gl_confdir, "prefs", NULL );
     276}
     277
     278static void
     279translate_08_to_09( const char* oldfile, const char* newfile )
     280{
     281    static struct pref_entry {
     282        const char* oldkey;
     283        const char* newkey;
     284    } pref_table[] = {
     285        { "add-behavior-ipc",       "add-behavior-ipc"},
     286        { "add-behavior-standard",  "add-behavior-standard"},
     287        { "download-directory",     "default-download-directory"},
     288        { "download-limit",         "download-limit"},
     289        { "use-download-limit",     "download-limit-enabled" },
     290        { "listening-port",         "listening-port"},
     291        { "use-nat-traversal",      "nat-traversal-enabled"},
     292        { "use-peer-exchange",      "pex-enabled"},
     293        { "ask-quit",               "prompt-before-exit"},
     294        { "ask-download-directory", "prompt-for-download-directory"},
     295        { "use-tray-icon",          "system-tray-icon-enabled"},
     296        { "upload-limit",           "upload-limit"},
     297        { "use-upload-limit",       "upload-limit-enabled"}
     298    };
     299
     300    GString * out = g_string_new( NULL );
     301    gchar * contents = NULL;
     302    gsize contents_len = 0;
     303    benc_val_t top;
     304
     305    memset( &top, 0, sizeof(benc_val_t) );
     306
     307    if( g_file_get_contents( oldfile, &contents, &contents_len, NULL )
     308        && !tr_bencLoad( contents, contents_len, &top, NULL )
     309        && top.type==TYPE_DICT )
     310    {
     311        unsigned int i;
     312        g_string_append( out, "\n[general]\n" );
     313        for ( i=0; i<G_N_ELEMENTS(pref_table); ++i ) {
     314            const benc_val_t * val = tr_bencDictFind( &top, pref_table[i].oldkey );
     315            if( val != NULL ) {
     316                const char * valstr = val->val.s.s;
     317                if( !strcmp( valstr, "yes" ) ) valstr = "true";
     318                if( !strcmp( valstr, "no" ) ) valstr = "false";
     319                g_string_append_printf( out, "%s=%s\n", pref_table[i].newkey, valstr );
     320            }
     321        }
     322    }
     323
     324    g_file_set_contents( newfile, out->str, out->len, NULL );
     325    g_string_free( out, TRUE );
     326    g_free( contents );
     327}
     328
     329void
     330cf_check_older_configs( void )
     331{
     332    char * cfn = getPrefsFilename( );
     333    char * cfn08 = getCompat08PrefsFilename( );
     334
     335    if( !g_file_test( cfn,   G_FILE_TEST_IS_REGULAR )
     336      && g_file_test( cfn08, G_FILE_TEST_IS_REGULAR ) )
     337        translate_08_to_09( cfn08, cfn );
     338
     339    g_free( cfn08 );
     340    g_free( cfn );
     341}
  • branches/0.9x/gtk/conf.h

    r3381 r4037  
    5656char *
    5757cf_sockname(void);
     58void
     59cf_check_older_configs(void);
    5860
    5961#endif /* TG_CONF_H */
  • branches/0.9x/gtk/main.c

    r3943 r4037  
    5454#include <libtransmission/transmission.h>
    5555#include <libtransmission/version.h>
    56 
    57 /* time in seconds to wait for torrents to stop when exiting */
    58 #define TRACKER_EXIT_TIMEOUT    10
    5956
    6057/* interval in milliseconds to update the torrent list display */
     
    434431}
    435432
     433/* since there are no buttons in the dialog, gtk tries to
     434 * select one of the labels, which looks ugly... so force
     435 * the dialog's primary and secondary labels to be unselectable */
     436static void
     437deselectLabels( GtkWidget * w, gpointer unused UNUSED )
     438{
     439    if( GTK_IS_LABEL( w ) )
     440        gtk_label_set_selectable( GTK_LABEL(w), FALSE );
     441    else if( GTK_IS_CONTAINER( w ) )
     442        gtk_container_foreach( GTK_CONTAINER(w), deselectLabels, NULL );
     443}
     444
    436445static void
    437446wannaquit( void * vdata )
    438447{
     448    GtkWidget * w;
     449#if GTK_CHECK_VERSION(2,10,0)
     450    GtkWidget * i;
     451#endif
    439452    struct cbdata * cbdata = vdata;
    440453
     
    445458    }
    446459
     460    w = gtk_message_dialog_new( cbdata->wind,
     461                                GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT,
     462                                GTK_MESSAGE_INFO,
     463                                GTK_BUTTONS_NONE,
     464                                _("Closing Connections" ) );
     465#if GTK_CHECK_VERSION(2,10,0)
     466    i = gtk_image_new_from_stock( GTK_STOCK_NETWORK, GTK_ICON_SIZE_DIALOG );
     467    gtk_widget_show( i );
     468    gtk_message_dialog_set_image( GTK_MESSAGE_DIALOG(w), i );
     469#endif
     470    gtk_message_dialog_format_secondary_text( GTK_MESSAGE_DIALOG(w),
     471                                              _("Sending upload/download totals to tracker..." ) );
     472    gtk_container_foreach( GTK_CONTAINER(GTK_DIALOG(w)->vbox), deselectLabels, NULL );
     473    gtk_widget_show( w );
     474
    447475    /* clear the UI */
    448476    gtk_list_store_clear( GTK_LIST_STORE( tr_core_model( cbdata->core ) ) );
    449477    gtk_widget_set_sensitive( GTK_WIDGET( cbdata->wind ), FALSE );
     478
     479   
    450480
    451481    /* shut down libT */
     
    698728setpex( tr_torrent * tor, void * arg )
    699729{
    700     gboolean * val;
    701 
    702     val = arg;
    703     tr_torrentDisablePex( tor, !(*val) );
     730    const gboolean * val = arg;
     731    tr_torrentDisablePex( tor, !*val );
    704732}
    705733
  • branches/0.9x/gtk/makemeta-ui.c

    r3466 r4037  
    7777    fraction = (double)ui->builder->pieceIndex / denom;
    7878    gtk_progress_bar_set_fraction( p, fraction );
    79     g_snprintf( buf, sizeof(buf), "%s (%d%%)", ui->torrent_name, (int)(fraction*100 + 0.5));
     79    g_snprintf( buf, sizeof(buf), "%s (%d%%)", ui->torrent_name, (int)(fraction*100));
    8080    gtk_progress_bar_set_text( p, buf );
    8181
  • branches/0.9x/gtk/torrent-inspector.c

    r3899 r4037  
    286286                      PEER_COL_CLIENT, client,
    287287                      PEER_COL_IS_ENCRYPTED, peer->isEncrypted,
    288                       PEER_COL_PROGRESS, (int)(100.0*peer->progress + 0.5),
     288                      PEER_COL_PROGRESS, (int)(100.0*peer->progress),
    289289                      PEER_COL_IS_DOWNLOADING, peer->isDownloading,
    290290                      PEER_COL_DOWNLOAD_RATE, peer->downloadFromRate,
     
    354354               gpointer             data UNUSED )
    355355{
     356    GString * gstr = g_string_new( NULL );
    356357    int status;
    357     const char * text;
    358358    gtk_tree_model_get( tree_model, iter, PEER_COL_STATUS, &status, -1 );
    359     switch( status )
     359
     360    if( status & TR_PEER_STATUS_HANDSHAKE )
    360361    {
    361         case TR_PEER_STATUS_HANDSHAKE:            text = _( "Handshaking" ); break;
    362         case TR_PEER_STATUS_PEER_IS_CHOKED:       text = _( "Peer is Choked" ); break;
    363         case TR_PEER_STATUS_CLIENT_IS_CHOKED:     text = _( "Choked" ); break;
    364         case TR_PEER_STATUS_CLIENT_IS_INTERESTED: text = _( "Choked & Interested" ); break;
    365         case TR_PEER_STATUS_READY:                text = _( "Ready" ); break;
    366         case TR_PEER_STATUS_REQUEST_SENT:         text = _( "Request Sent" ); break;
    367         case TR_PEER_STATUS_ACTIVE           :    text = _( "Active" ); break;
    368         case TR_PEER_STATUS_ACTIVE_AND_CHOKED:    text = _( "Active & Choked" ); break;
    369         default:                                  text = "BUG"; break;
     362        g_string_append( gstr, _("Handshaking") );
    370363    }
    371     g_object_set (renderer, "text", text, NULL);
     364    else
     365    {
     366        if( status & TR_PEER_STATUS_CLIENT_IS_SENDING )
     367            g_string_append( gstr, _("Uploading to peer") );
     368        else if( status & TR_PEER_STATUS_PEER_IS_INTERESTED )
     369            g_string_append( gstr, _("Peer wants our data") );
     370        else if( status & TR_PEER_STATUS_PEER_IS_CHOKED )
     371            g_string_append( gstr, _("Refusing to send data to peer") );
     372
     373        g_string_append( gstr, " - " );
     374
     375        if( status & TR_PEER_STATUS_PEER_IS_SENDING )
     376            g_string_append( gstr, _("Downloading from peer") );
     377        else if( status & TR_PEER_STATUS_CLIENT_SENT_REQUEST )
     378            g_string_append( gstr, _("Requesting data from peer") );
     379        else if( status & TR_PEER_STATUS_CLIENT_IS_INTERESTED )
     380            g_string_append( gstr, _("Waiting to request data from peer") );
     381        else if( status & TR_PEER_STATUS_CLIENT_IS_CHOKED )
     382            g_string_append( gstr, _("Peer will not send us data") );
     383    }
     384
     385    g_object_set( renderer, "text", gstr->str, NULL );
     386    g_string_free( gstr, TRUE );
    372387}
    373388
     
    665680    gtk_box_pack_start (GTK_BOX(vbox), w, FALSE, FALSE, 0);
    666681
    667     g_snprintf (name, sizeof(name), "<b>%s</b>", _("Peers"));
     682    g_snprintf (name, sizeof(name), "<b>%s</b>", _("Connected Peers"));
    668683    l = gtk_label_new (NULL);
    669684    gtk_misc_set_alignment (GTK_MISC(l), 0.0f, 0.5f);
     
    11551170                                              -1 );
    11561171            g_assert( 0 <= index );
    1157             percent = (int)(fileStats[index].progress * 100.0 + 0.5); /* [0...100] */
     1172            percent = (int)(fileStats[index].progress * 100.0); /* [0...100] */
    11581173            subGot = (guint64)(subTotal * percent/100.0);
    11591174        }
     
    11651180           because that causes the "priorities" combobox to pop down */
    11661181        gtk_tree_model_get (model, &iter, FC_PROG, &oldProg, -1);
    1167         newProg = (int)(100.0*subGot/subTotal + 0.5);
     1182        newProg = (int)(100.0*subGot/subTotal);
    11681183        if (oldProg != newProg)
    11691184          gtk_tree_store_set (store, &iter,
    1170                               FC_PROG, (int)(100.0*subGot/subTotal + 0.5), -1);
     1185                              FC_PROG, (int)(100.0*subGot/subTotal), -1);
    11711186
    11721187        gotSize += subGot;
  • branches/0.9x/gtk/tr_core.c

    r3943 r4037  
    350350tr_core_insert( TrCore * self, TrTorrent * tor )
    351351{
    352     GtkTreeIter iter;
    353     const tr_info * inf;
    354 
    355     gtk_list_store_append( GTK_LIST_STORE( self->model ), &iter );
    356     inf = tr_torrent_info( tor );
    357     gtk_list_store_set( GTK_LIST_STORE( self->model ), &iter,
    358                         MC_NAME,    inf->name,
    359                         MC_SIZE,    inf->totalSize,
    360                         MC_HASH,    inf->hashString,
    361                         MC_TORRENT, tor,
    362                         MC_ID,      self->nextid,
    363                         -1);
     352    const tr_info * inf = tr_torrent_info( tor );
     353    gtk_list_store_insert_with_values( GTK_LIST_STORE( self->model ), NULL, 0,
     354                                       MC_NAME,    inf->name,
     355                                       MC_SIZE,    inf->totalSize,
     356                                       MC_HASH,    inf->hashString,
     357                                       MC_TORRENT, tor,
     358                                       MC_ID,      self->nextid,
     359                                       -1);
    364360    g_object_unref( tor );
    365361    self->nextid++;
  • branches/0.9x/gtk/tr_prefs.c

    r3548 r4037  
    2727tr_prefs_init_global( void )
    2828{
     29    cf_check_older_configs( );
     30
    2931    pref_flag_set_default   ( PREF_KEY_DL_LIMIT_ENABLED, FALSE );
    3032    pref_int_set_default    ( PREF_KEY_DL_LIMIT, 100 );
  • branches/0.9x/gtk/tr_window.c

    r3899 r4037  
    9393    else if( TR_STATUS_DOWNLOAD & status )
    9494    {
    95         bottom = g_strdup_printf( ngettext( "Downloading from %i of %i peer",
    96                                             "Downloading from %i of %i peers",
     95        bottom = g_strdup_printf( ngettext( "Downloading from %i of %i connections",
     96                                            "Downloading from %i of %i connections",
    9797                                            tpeers ), dpeers, tpeers );
    9898    }
  • branches/0.9x/libtransmission/bencode.c

    r3897 r4037  
    3535#include "utils.h"
    3636
     37/**
     38***
     39**/
     40
     41static int
     42tr_bencIsInt ( const benc_val_t * val ) {
     43    return val!=NULL && val->type==TYPE_INT;
     44}
     45
     46static int
     47tr_bencIsList( const benc_val_t * val ) {
     48    return val!=NULL && val->type==TYPE_LIST;
     49}
     50
     51static int
     52tr_bencIsDict( const benc_val_t * val ) {
     53    return val!=NULL && val->type==TYPE_DICT;
     54}
     55
     56/**
     57***
     58**/
     59
    3760/* setting to 1 to help expose bugs with tr_bencListAdd and tr_bencDictAdd */
    3861#define LIST_SIZE   20 /* number of items to increment list/dict buffer by */
     
    195218    {
    196219        case TYPE_INT:
    197             fprintf( stderr, "int:  %"PRId64"\n", val->val.i );
     220            fprintf( stderr, "int:  %"PRId64"\n", tr_bencGetInt(val) );
    198221            break;
    199222
     
    414437    {
    415438        case TYPE_INT:
    416             evbuffer_add_printf( out, "i%"PRId64"e", val->val.i );
     439            evbuffer_add_printf( out, "i%"PRId64"e", tr_bencGetInt(val) );
    417440            break;
    418441
     
    471494**/
    472495
    473 int
    474 tr_bencIsStr ( const benc_val_t * val )
    475 {
    476     return val!=NULL && val->type==TYPE_STR;
    477 }
    478 
    479 int
    480 tr_bencIsInt ( const benc_val_t * val )
    481 {
    482     return val!=NULL && val->type==TYPE_INT;
    483 }
    484 
    485 int
    486 tr_bencIsList( const benc_val_t * val )
    487 {
    488     return val!=NULL && val->type==TYPE_LIST;
    489 }
    490 
    491 int
    492 tr_bencIsDict( const benc_val_t * val )
    493 {
    494     return val!=NULL && val->type==TYPE_DICT;
    495 }
     496benc_val_t*
     497tr_bencDictFindType( benc_val_t * val, const char * key, int type )
     498{
     499    benc_val_t * ret = tr_bencDictFind( val, key );
     500    return ret && ret->type == type ? ret : NULL;
     501}
     502
     503int64_t
     504tr_bencGetInt ( const benc_val_t * val )
     505{
     506    assert( tr_bencIsInt( val ) );
     507    return val->val.i;
     508}
  • branches/0.9x/libtransmission/bencode.h

    r3897 r4037  
    6262void         tr_bencFree( benc_val_t * val );
    6363benc_val_t * tr_bencDictFind( benc_val_t * val, const char * key );
     64benc_val_t * tr_bencDictFindType( benc_val_t * val, const char * key, int type );
    6465benc_val_t * tr_bencDictFindFirst( benc_val_t * val, ... );
    6566
     
    8990char*  tr_bencSave( const benc_val_t * val, int * len );
    9091
    91 int    tr_bencIsStr   ( const benc_val_t * val );
    92 int    tr_bencIsInt   ( const benc_val_t * val );
    93 int    tr_bencIsList  ( const benc_val_t * val );
    94 int    tr_bencIsDict  ( const benc_val_t * val );
     92int64_t  tr_bencGetInt ( const benc_val_t * val );
    9593
    9694#endif
  • branches/0.9x/libtransmission/handshake.c

    r3897 r4037  
    969969{
    970970    dbgmsg( handshake, "handshakeDone: %s", isOK ? "connected" : "aborting" );
    971     tr_peerIoSetIOFuncs( handshake->io, NULL, NULL, NULL );
     971    tr_peerIoSetIOFuncs( handshake->io, NULL, NULL, NULL, NULL );
    972972
    973973    fireDoneFunc( handshake, isOK );
     
    10311031    handshake->handle = tr_peerIoGetHandle( io );
    10321032   
    1033     tr_peerIoSetIOFuncs( handshake->io, canRead, gotError, handshake );
     1033    tr_peerIoSetIOFuncs( handshake->io, canRead, NULL, gotError, handshake );
    10341034
    10351035    if( tr_peerIoIsIncoming( handshake->io ) )
  • branches/0.9x/libtransmission/internal.h

    r3941 r4037  
    4444#endif
    4545
     46int tr_torrentIsPrivate( const tr_torrent * );
    4647
    4748void tr_torrentRecheckCompleteness( tr_torrent * );
     
    193194
    194195    uint8_t                    isClosed;
     196
     197    struct tr_stats_handle   * sessionStats;
     198    struct tr_tracker_handle * tracker;
    195199};
    196200
  • branches/0.9x/libtransmission/natpmp.c

    r3706 r4037  
    4242
    4343#define PMP_PORT                5351
    44 #define PMP_MCAST_ADDR          "224.0.0.1"
    4544#define PMP_INITIAL_DELAY       250     /* ms, 1/4 second */
    4645#define PMP_TOTAL_DELAY         120000  /* ms, 2 minutes */
     
    8483} tr_natpmp_req_t;
    8584
    86 struct tr_natpmp_s
     85struct tr_natpmp
    8786{
    8887#define PMP_STATE_IDLE          1
     
    115114
    116115static void
    117 unmap( tr_natpmp_t * pmp );
     116unmap( tr_natpmp * pmp );
    118117static int
    119118checktime( tr_natpmp_uptime_t * uptime, uint32_t seen );
     
    127126resetreq( tr_natpmp_req_t * req );
    128127static tr_tristate_t
    129 pulsereq( tr_natpmp_t * req );
     128pulsereq( tr_natpmp * req );
    130129static int
    131130sendreq( tr_natpmp_req_t * req );
     
    133132mcastsetup();
    134133static void
    135 mcastpulse( tr_natpmp_t * pmp );
     134mcastpulse( tr_natpmp * pmp );
    136135static tr_tristate_t
    137136parseresponse( uint8_t * buf, int len, int port, tr_natpmp_parse_t * parse );
    138137
    139 tr_natpmp_t *
     138tr_natpmp *
    140139tr_natpmpInit()
    141140{
    142     tr_natpmp_t * pmp;
     141    tr_natpmp * pmp;
    143142
    144143    pmp = calloc( 1, sizeof( *pmp ) );
     
    171170
    172171void
    173 tr_natpmpStart( tr_natpmp_t * pmp )
     172tr_natpmpStart( tr_natpmp * pmp )
    174173{
    175174    if( !pmp->active )
     
    185184
    186185void
    187 tr_natpmpStop( tr_natpmp_t * pmp )
     186tr_natpmpStop( tr_natpmp * pmp )
    188187{
    189188    if( pmp->active )
     
    197196
    198197int
    199 tr_natpmpStatus( tr_natpmp_t * pmp )
     198tr_natpmpStatus( tr_natpmp * pmp )
    200199{
    201200    int ret;
     
    240239
    241240void
    242 tr_natpmpForwardPort( tr_natpmp_t * pmp, int port )
     241tr_natpmpForwardPort( tr_natpmp * pmp, int port )
    243242{
    244243    tr_inf( "nat-pmp set port %i", port );
     
    247246
    248247void
    249 tr_natpmpRemoveForwarding( tr_natpmp_t * pmp )
     248tr_natpmpRemoveForwarding( tr_natpmp * pmp )
    250249{
    251250    tr_inf( "nat-pmp unset port" );
     
    255254
    256255void
    257 tr_natpmpClose( tr_natpmp_t * pmp )
     256tr_natpmpClose( tr_natpmp * pmp )
    258257{
    259258    /* try to send at least one delete request if we have a port mapping */
     
    266265
    267266void
    268 tr_natpmpPulse( tr_natpmp_t * pmp, int * publicPort )
     267tr_natpmpPulse( tr_natpmp * pmp, int * publicPort )
    269268{
    270269    if( 0 <= pmp->mcastfd )
     
    444443
    445444void
    446 unmap( tr_natpmp_t * pmp )
     445unmap( tr_natpmp * pmp )
    447446{
    448447    switch( pmp->state )
     
    568567
    569568static tr_tristate_t
    570 pulsereq( tr_natpmp_t * pmp )
     569pulsereq( tr_natpmp * pmp )
    571570{
    572571    tr_natpmp_req_t  * req = pmp->req;
     
    693692
    694693static void
    695 mcastpulse( tr_natpmp_t * pmp )
     694mcastpulse( tr_natpmp * pmp )
    696695{
    697696    struct sockaddr_in sin;
  • branches/0.9x/libtransmission/natpmp.h

    r1720 r4037  
    2626#define TR_NATPMP_H 1
    2727
    28 typedef struct tr_natpmp_s tr_natpmp_t;
     28typedef struct tr_natpmp tr_natpmp;
    2929
    30 tr_natpmp_t * tr_natpmpInit();
    31 void        tr_natpmpStart( tr_natpmp_t * );
    32 void        tr_natpmpStop( tr_natpmp_t * );
    33 int         tr_natpmpStatus( tr_natpmp_t * );
    34 void        tr_natpmpForwardPort( tr_natpmp_t *, int );
    35 void        tr_natpmpRemoveForwarding( tr_natpmp_t * );
    36 void        tr_natpmpPulse( tr_natpmp_t *, int * );
    37 void        tr_natpmpClose( tr_natpmp_t * );
     30tr_natpmp * tr_natpmpInit();
     31void        tr_natpmpStart( tr_natpmp * );
     32void        tr_natpmpStop( tr_natpmp * );
     33int         tr_natpmpStatus( tr_natpmp * );
     34void        tr_natpmpForwardPort( tr_natpmp *, int );
     35void        tr_natpmpRemoveForwarding( tr_natpmp * );
     36void        tr_natpmpPulse( tr_natpmp *, int * );
     37void        tr_natpmpClose( tr_natpmp * );
     38
     39#define PMP_MCAST_ADDR "224.0.0.1"
    3840
    3941#endif
  • branches/0.9x/libtransmission/net.c

    r3553 r4037  
    4141#include "transmission.h"
    4242#include "fdlimit.h"
     43#include "natpmp.h"
    4344#include "net.h"
    4445#include "platform.h"
     
    211212    memset( &sock, 0, sizeof( sock ) );
    212213    sock.sin_family      = AF_INET;
    213     sock.sin_addr.s_addr = INADDR_ANY;
     214
     215    /* Leopard closes a SO_REUSEADDR + INADDR_ANY hole, so we can't
     216     * use INADDR_ANY when binding for nat-pmp. For details, refer to
     217     * http://www.uwsg.indiana.edu/hypermail/linux/kernel/9902.1/0828.html .
     218     * This can probably be done cleaner, but since we're only using SOCK_DGRAM
     219     * for nat-pmp, this quick fix should work. */
     220    if ( SOCK_DGRAM == type )
     221        sock.sin_addr.s_addr = inet_addr( PMP_MCAST_ADDR );
     222    else
     223        sock.sin_addr.s_addr = INADDR_ANY;
     224
    214225    sock.sin_port        = htons( port );
    215226
  • branches/0.9x/libtransmission/peer-io.c

    r3897 r4037  
    6262
    6363    tr_can_read_cb     canRead;
     64    tr_did_write_cb    didWrite;
    6465    tr_net_error_cb    gotError;
    6566    void             * userData;
     
    7172***
    7273**/
     74
     75static void
     76didWriteWrapper( struct bufferevent * e, void * userData )
     77{
     78    tr_peerIo * c = (tr_peerIo *) userData;
     79    if( c->didWrite != NULL )
     80        (*c->didWrite)( e, c->userData );
     81}
    7382
    7483static void
     
    135144    c->bufev = bufferevent_new( c->socket,
    136145                                canReadWrapper,
    137                                 NULL,
     146                                didWriteWrapper,
    138147                                gotErrorWrapper,
    139148                                c );
     
    193202    {
    194203        io->canRead = NULL;
     204        io->didWrite = NULL;
    195205        io->gotError = NULL;
    196206        tr_runInEventThread( io->handle, io_dtor, io );
     
    242252tr_peerIoSetIOFuncs( tr_peerIo          * io,
    243253                     tr_can_read_cb       readcb,
     254                     tr_did_write_cb      writecb,
    244255                     tr_net_error_cb      errcb,
    245256                     void               * userData )
    246257{
    247258    io->canRead = readcb;
     259    io->didWrite = writecb;
    248260    io->gotError = errcb;
    249261    io->userData = userData;
     
    273285
    274286        io->bufev = bufferevent_new( io->socket,
    275                                      canReadWrapper, NULL, gotErrorWrapper,
     287                                     canReadWrapper,
     288                                     didWriteWrapper,
     289                                     gotErrorWrapper,
    276290                                     io );
    277291        bufferevent_settimeout( io->bufev, io->timeout, io->timeout );
  • branches/0.9x/libtransmission/peer-io.h

    r3897 r4037  
    100100typedef enum { READ_MORE, READ_AGAIN, READ_DONE } ReadState;
    101101typedef ReadState (*tr_can_read_cb)(struct bufferevent*, void* user_data);
     102typedef void (*tr_did_write_cb)(struct bufferevent *, void *);
    102103typedef void (*tr_net_error_cb)(struct bufferevent *, short what, void *);
    103104
    104105void  tr_peerIoSetIOFuncs( tr_peerIo        * io,
    105106                           tr_can_read_cb     readcb,
     107                           tr_did_write_cb    writecb,
    106108                           tr_net_error_cb    errcb,
    107109                           void             * user_data );
  • branches/0.9x/libtransmission/peer-mgr.c

    r3959 r4037  
    7575
    7676    /* how frequently to decide which peers live and die */
    77     RECONNECT_PERIOD_MSEC = (6 * 1000),
     77    RECONNECT_PERIOD_MSEC = (2 * 1000),
    7878
    7979    /* how frequently to refill peers' request lists */
     
    9898    /* how many peers to unchoke per-torrent. */
    9999    /* FIXME: make this user-configurable? */
    100     NUM_UNCHOKED_PEERS_PER_TORRENT = 10, /* arbitrary */
     100    NUM_UNCHOKED_PEERS_PER_TORRENT = 16, /* arbitrary */
    101101
    102102    /* set this too high and there will be a lot of churn.
    103103     * set it too low and you'll get peers too slowly */
    104     MAX_RECONNECTIONS_PER_PULSE = 3,
     104    MAX_RECONNECTIONS_PER_PULSE = 2,
    105105
    106106    /* corresponds to ut_pex's added.f flags */
  • branches/0.9x/libtransmission/peer-msgs.c

    r3930 r4037  
    7171    PEER_PULSE_INTERVAL     = (100),       /* msec between calls to pulse() */
    7272    RATE_PULSE_INTERVAL     = (250),       /* msec between calls to ratePulse() */
     73
     74    MAX_OUTBUF_SIZE         = 4096,
    7375     
    7476    /* Fast Peers Extension constants */
     
    140142    time_t clientSentPexAt;
    141143    time_t clientSentAnythingAt;
     144
     145    time_t clientSentPieceDataAt;
     146    time_t peerSentPieceDataAt;
    142147
    143148    unsigned int peerSentBitfield         : 1;
     
    782787
    783788    /* does the peer prefer encrypted connections? */
    784     sub = tr_bencDictFind( &val, "e" );
    785     if( tr_bencIsInt( sub ) )
     789    if(( sub = tr_bencDictFindType( &val, "e", TYPE_INT )))
    786790        msgs->info->encryption_preference = sub->val.i
    787791                                      ? ENCRYPTION_PREFERENCE_YES
     
    789793
    790794    /* check supported messages for utorrent pex */
    791     sub = tr_bencDictFind( &val, "m" );
    792     if( tr_bencIsDict( sub ) ) {
    793         sub = tr_bencDictFind( sub, "ut_pex" );
    794         if( tr_bencIsInt( sub ) ) {
     795    if(( sub = tr_bencDictFindType( &val, "m", TYPE_DICT ))) {
     796        if(( sub = tr_bencDictFindType( sub, "ut_pex", TYPE_INT ))) {
    795797            msgs->peerSupportsPex = 1;
    796798            msgs->ut_pex_id = (uint8_t) sub->val.i;
     
    800802
    801803    /* get peer's listening port */
    802     sub = tr_bencDictFind( &val, "p" );
    803     if( tr_bencIsInt( sub ) ) {
     804    if(( sub = tr_bencDictFindType( &val, "p", TYPE_INT ))) {
    804805        msgs->info->port = htons( (uint16_t)sub->val.i );
    805806        dbgmsg( msgs, "msgs->port is now %hu", msgs->info->port );
     
    822823    tr_peerIoReadBytes( msgs->io, inbuf, tmp, msglen );
    823824
    824     if( tr_bencLoad( tmp, msglen, &val, NULL ) || !tr_bencIsDict( &val ) ) {
     825    if( tr_bencLoad( tmp, msglen, &val, NULL ) || ( val.type != TYPE_DICT ) ) {
    825826        dbgmsg( msgs, "GET can't read extended-pex dictionary" );
    826827        tr_free( tmp );
     
    828829    }
    829830
    830     sub = tr_bencDictFind( &val, "added" );
    831     if( tr_bencIsStr(sub) && ((sub->val.s.i % 6) == 0)) {
     831    if(( sub = tr_bencDictFindType( &val, "added", TYPE_STR ))) {
    832832        const int n = sub->val.s.i / 6 ;
    833833        dbgmsg( msgs, "got %d peers from uT pex", n );
     
    10361036    tor->activityDate = tr_date( );
    10371037    tor->downloadedCur += byteCount;
     1038    msgs->peerSentPieceDataAt = time( NULL );
    10381039    msgs->info->pieceDataActivityDate = time( NULL );
    10391040    msgs->info->credit += (int)(byteCount * SWIFT_REPAYMENT_RATIO);
     
    10421043    tr_rcTransferred( tor->handle->download, byteCount );
    10431044}
    1044 
    10451045
    10461046static int
     
    12601260    tor->activityDate = tr_date( );
    12611261    tor->uploadedCur += byteCount;
     1262    msgs->clientSentPieceDataAt = time( NULL );
    12621263    msgs->info->pieceDataActivityDate = time( NULL );
    12631264    msgs->info->credit -= byteCount;
     
    14051406
    14061407    return 0;
     1408}
     1409
     1410static void
     1411didWrite( struct bufferevent * evin UNUSED, void * vmsgs )
     1412{
     1413    pulse( vmsgs );
    14071414}
    14081415
     
    14441451**/
    14451452
     1453static int
     1454isSwiftEnabled( const tr_peermsgs * msgs )
     1455{
     1456    /* rationale: SWIFT is good for getting rid of deadbeats, but most
     1457     * private trackers have ratios where you _want_ to feed deadbeats
     1458     * as much as possible.  So we disable SWIFT on private torrents */
     1459    return SWIFT_ENABLED
     1460        && !tr_torrentIsSeed( msgs->torrent )
     1461        && !tr_torrentIsPrivate( msgs->torrent );
     1462}
     1463
    14461464static size_t
    14471465getUploadMax( const tr_peermsgs * msgs )
     
    14491467    static const size_t maxval = ~0;
    14501468    const tr_torrent * tor = msgs->torrent;
    1451     const int useSwift = SWIFT_ENABLED && !tr_torrentIsSeed( msgs->torrent );
     1469    const int useSwift = isSwiftEnabled( msgs );
    14521470    const size_t swiftLeft = msgs->info->credit;
    14531471    size_t speedLeft;
     
    14621480        speedLeft = ~0;
    14631481
    1464     bufLeft = 4096 - tr_peerIoWriteBytesWaiting( msgs->io );
     1482    bufLeft = MAX_OUTBUF_SIZE - tr_peerIoWriteBytesWaiting( msgs->io );
    14651483    ret = MIN( speedLeft, bufLeft );
    14661484    if( useSwift)
     
    14931511updatePeerStatus( tr_peermsgs * msgs )
    14941512{
     1513    const time_t now = time( NULL );
    14951514    tr_peer * peer = msgs->info;
     1515    tr_peer_status status = 0;
    14961516
    14971517    if( !msgs->peerSentBitfield )
    1498         peer->status = TR_PEER_STATUS_HANDSHAKE;
    1499 
    1500     else if( ( time(NULL) - peer->pieceDataActivityDate ) < 3 )
    1501         peer->status = peer->clientIsChoked
    1502                        ? TR_PEER_STATUS_ACTIVE_AND_CHOKED
    1503                        : TR_PEER_STATUS_ACTIVE;
    1504 
    1505     else if( peer->peerIsChoked )
    1506         peer->status = TR_PEER_STATUS_PEER_IS_CHOKED;
    1507 
    1508     else if( peer->clientIsChoked )
    1509         peer->status = peer->clientIsInterested
    1510                        ? TR_PEER_STATUS_CLIENT_IS_INTERESTED
    1511                        : TR_PEER_STATUS_CLIENT_IS_CHOKED;
    1512 
    1513     else if( msgs->clientAskedFor != NULL )
    1514         peer->status = TR_PEER_STATUS_REQUEST_SENT;
    1515 
    1516     else
    1517         peer->status = TR_PEER_STATUS_READY;
     1518        status |= TR_PEER_STATUS_HANDSHAKE;
     1519
     1520    if( msgs->info->peerIsChoked )
     1521        status |= TR_PEER_STATUS_PEER_IS_CHOKED;
     1522
     1523    if( msgs->info->peerIsInterested )
     1524        status |= TR_PEER_STATUS_PEER_IS_INTERESTED;
     1525
     1526    if( msgs->info->clientIsChoked )
     1527        status |= TR_PEER_STATUS_CLIENT_IS_CHOKED;
     1528
     1529    if( msgs->info->clientIsInterested )
     1530        status |= TR_PEER_STATUS_CLIENT_IS_INTERESTED;
     1531
     1532    if( ( now - msgs->clientSentPieceDataAt ) < 3 )
     1533        status |= TR_PEER_STATUS_CLIENT_IS_SENDING;
     1534
     1535    if( ( now - msgs->peerSentPieceDataAt ) < 3 )
     1536        status |= TR_PEER_STATUS_PEER_IS_SENDING;
     1537
     1538    if( msgs->clientAskedFor != NULL )
     1539        status |= TR_PEER_STATUS_CLIENT_SENT_REQUEST;
     1540
     1541    peer->status = status;
    15181542}
    15191543
     
    18231847   
    18241848    tr_peerIoSetTimeoutSecs( m->io, 150 ); /* timeout after N seconds of inactivity */
    1825     tr_peerIoSetIOFuncs( m->io, canRead, gotError, m );
     1849    tr_peerIoSetIOFuncs( m->io, canRead, didWrite, gotError, m );
    18261850    ratePulse( m );
    18271851
  • branches/0.9x/libtransmission/shared.c

    r3797 r4037  
    5353
    5454    /* NAT-PMP/UPnP */
    55     tr_natpmp_t  * natpmp;
     55    tr_natpmp    * natpmp;
    5656    tr_upnp_t    * upnp;
    5757};
  • branches/0.9x/libtransmission/torrent.c

    r3933 r4037  
    643643
    644644int
     645tr_torrentIsPrivate( const tr_torrent * tor )
     646{
     647    return tor
     648        && tor->info.isPrivate;
     649}
     650
     651int
    645652tr_torrentIsPexEnabled( const tr_torrent * tor )
    646653{
    647     return !tor->info.isPrivate && !tor->pexDisabled;
     654    return tor
     655        && !tr_torrentIsPrivate( tor )
     656        && !tor->pexDisabled;
    648657}
    649658
  • branches/0.9x/libtransmission/tracker.c

    r3938 r4037  
    2525#include "bencode.h"
    2626#include "completion.h"
     27#include "list.h"
    2728#include "net.h"
    2829#include "publish.h"
    2930#include "shared.h"
    3031#include "tracker.h"
     32#include "trcompat.h" /* for asprintf */
    3133#include "trevent.h"
    3234#include "utils.h"
     
    3436enum
    3537{
     38    /* seconds between tracker pulses */
     39    PULSE_INTERVAL_MSEC = 1000,
     40
     41    /* maximum number of concurrent tracker socket connections */
     42    MAX_TRACKER_SOCKETS = 16,
     43
    3644    /* unless the tracker says otherwise, rescrape this frequently */
    3745    DEFAULT_SCRAPE_INTERVAL_SEC = (60 * 15),
     
    101109    char * trackerID;
    102110
    103     /* the last tracker request we sent. (started, stopped, etc.)
    104        automatic announces are an empty string;
    105        NULL means no message has ever been sent */
    106     char * lastRequest;
    107 
    108111    time_t manualAnnounceAllowedAt;
    109 
    110     tr_timer * scrapeTimer;
    111     tr_timer * reannounceTimer;
    112 
    113     unsigned int isRunning : 1;
     112    time_t reannounceAt;
     113    time_t scrapeAt;
     114
     115    unsigned int isRunning     : 1;
    114116};
    115117
     
    145147#define dbgmsg(t, fmt...) myDebug(__FILE__, __LINE__, t, ##fmt )
    146148
    147 
    148149/***
    149 ****  Connections that know how to clean up after themselves
     150****
    150151***/
    151152
     153static tr_tracker_info *
     154getCurrentAddress( const tr_tracker * t )
     155{
     156    assert( t->addresses != NULL );
     157    assert( t->addressIndex >= 0 );
     158    assert( t->addressIndex < t->addressCount );
     159
     160    return t->redirect ? t->redirect
     161                       : t->addresses + t->addressIndex;
     162}
     163
    152164static int
    153 freeConnection( void * evcon )
    154 {
    155     evhttp_connection_free( evcon );
    156     return FALSE;
    157 }
    158 
    159 static void
    160 connectionClosedCB( struct evhttp_connection * evcon, void * handle )
    161 {
    162     /* libevent references evcon right after calling this function,
    163        so we can't free it yet... defer it to after this call chain
    164        has played out */
    165     tr_timerNew( handle, freeConnection, evcon, 100 );
    166 }
    167 
    168 static struct evhttp_connection*
    169 getConnection( tr_tracker * t, const char * address, int port )
    170 {
    171     struct evhttp_connection * c = evhttp_connection_new( address, port );
    172     evhttp_connection_set_closecb( c, connectionClosedCB, t->handle );
    173     return c;
     165trackerSupportsScrape( const tr_tracker * t )
     166{
     167    const tr_tracker_info * info = getCurrentAddress( t );
     168
     169    return ( info != NULL )
     170        && ( info->scrape != NULL )
     171        && ( info->scrape[0] != '\0' );
     172}
     173
     174/***
     175****
     176***/
     177
     178struct torrent_hash
     179{
     180    tr_handle * handle;
     181    uint8_t hash[SHA_DIGEST_LENGTH];
     182};
     183
     184static struct torrent_hash*
     185torrentHashNew( tr_handle * handle, const tr_tracker * t )
     186{
     187    struct torrent_hash * data = tr_new( struct torrent_hash, 1 );
     188    data->handle = handle;
     189    memcpy( data->hash, t->hash, SHA_DIGEST_LENGTH );
     190    return data;
     191}
     192
     193tr_tracker *
     194findTrackerFromHash( struct torrent_hash * data )
     195{
     196    tr_torrent * torrent = tr_torrentFindFromHash( data->handle, data->hash );
     197    return torrent ? torrent->tracker : NULL;
     198}
     199
     200tr_tracker *
     201findTracker( tr_handle * handle, const uint8_t * hash )
     202{
     203    tr_torrent * torrent = tr_torrentFindFromHash( handle, hash );
     204    return torrent ? torrent->tracker : NULL;
    174205}
    175206
     
    183214publishMessage( tr_tracker * t, const char * msg, int type )
    184215{
    185     tr_tracker_event event = emptyEvent;
    186     event.hash = t->hash;
    187     event.messageType = type;
    188     event.text = msg;
    189     tr_publisherPublish( t->publisher, t, &event );
     216    if( t != NULL )
     217    {
     218        tr_tracker_event event = emptyEvent;
     219        event.hash = t->hash;
     220        event.messageType = type;
     221        event.text = msg;
     222        tr_publisherPublish( t->publisher, t, &event );
     223    }
    190224}
    191225
     
    197231
    198232static void
    199 publishErrorMessage( tr_tracker * t, const char * msg )
    200 {
     233publishErrorMessageAndStop( tr_tracker * t, const char * msg )
     234{
     235    /* not in the 0.9x branch... */
     236    /* t->isRunning = 0; */
    201237    publishMessage( t, msg, TR_TRACKER_ERROR );
    202238}
     
    221257
    222258/***
    223 ****  LIFE CYCLE
     259****
    224260***/
    225261
    226262static void
    227 generateKeyParam( char * msg, int len )
    228 {
    229     int i;
    230     const char * pool = "abcdefghijklmnopqrstuvwxyz0123456789";
    231     const int poolSize = strlen( pool );
    232     for( i=0; i<len; ++i )
    233         *msg++ = pool[tr_rand(poolSize)];
    234     *msg = '\0';
    235 }
    236 
    237 static void
    238 escape( char * out, const uint8_t * in, int in_len ) /* rfc2396 */
    239 {
    240     const uint8_t *end = in + in_len;
    241     while( in != end )
    242         if( isalnum(*in) )
    243             *out++ = (char) *in++;
    244         else
    245             out += snprintf( out, 4, "%%%02X", (unsigned int)*in++ );
    246     *out = '\0';
    247 }
    248 
    249 static void scrapeNow( tr_tracker * );
    250 
    251 tr_tracker *
    252 tr_trackerNew( const tr_torrent * torrent )
    253 {
    254     const tr_info * info = &torrent->info;
    255     int i, j, sum, *iwalk;
    256     tr_tracker_info * nwalk;
    257     tr_tracker * t;
    258 
    259     tr_dbg( "making a new tracker for \"%s\"", info->primaryAddress );
    260 
    261     t = tr_new0( tr_tracker, 1 );
    262     t->handle = torrent->handle;
    263     t->scrapeIntervalSec       = DEFAULT_SCRAPE_INTERVAL_SEC;
    264     t->announceIntervalSec     = DEFAULT_ANNOUNCE_INTERVAL_SEC;
    265     t->announceMinIntervalSec  = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
    266     generateKeyParam( t->key_param, KEYLEN );
    267 
    268     t->publisher = tr_publisherNew( );
    269     t->timesDownloaded = -1;
    270     t->seederCount = -1;
    271     t->leecherCount = -1;
    272     t->manualAnnounceAllowedAt = ~(time_t)0;
    273     t->name = tr_strdup( info->name );
    274     memcpy( t->hash, info->hash, SHA_DIGEST_LENGTH );
    275     escape( t->escaped, info->hash, SHA_DIGEST_LENGTH );
    276 
    277     for( sum=i=0; i<info->trackerTiers; ++i )
    278          sum += info->trackerList[i].count;
    279     t->addresses = nwalk = tr_new0( tr_tracker_info, sum );
    280     t->addressIndex = 0;
    281     t->addressCount = sum;
    282     t->tierFronts = iwalk = tr_new0( int, sum );
    283 
    284     for( i=0; i<info->trackerTiers; ++i )
    285     {
    286         const int tierFront = nwalk - t->addresses;
    287 
    288         for( j=0; j<info->trackerList[i].count; ++j )
    289         {
    290             const tr_tracker_info * src = &info->trackerList[i].list[j];
    291             nwalk->address = tr_strdup( src->address );
    292             nwalk->port = src->port;
    293             nwalk->announce = tr_strdup( src->announce );
    294             nwalk->scrape = tr_strdup( src->scrape );
    295             ++nwalk;
    296 
    297             *iwalk++ = tierFront;
    298         }
    299     }
    300 
    301     assert( nwalk - t->addresses == sum );
    302     assert( iwalk - t->tierFronts == sum );
    303 
    304     scrapeNow( t );
    305     return t;
    306 }
    307 
    308 static void
    309 onTrackerFreeNow( void * vt )
    310 {
    311     int i;
    312     tr_tracker * t = vt;
    313 
    314     tr_timerFree( &t->scrapeTimer );
    315     tr_timerFree( &t->reannounceTimer );
    316     tr_publisherFree( &t->publisher );
    317     tr_free( t->name );
    318     tr_free( t->trackerID );
    319     tr_free( t->lastRequest );
    320 
    321     /* addresses... */
    322     for( i=0; i<t->addressCount; ++i )
    323         tr_trackerInfoClear( &t->addresses[i] );
    324     tr_free( t->addresses );
    325     tr_free( t->tierFronts );
    326 
    327     /* redirect... */
    328     if( t->redirect ) {
    329         tr_trackerInfoClear( t->redirect );
    330         tr_free( t->redirect );
    331     }
    332 
    333     tr_free( t );
    334 }
    335 
    336 void
    337 tr_trackerFree( tr_tracker * t )
    338 {
    339     tr_runInEventThread( t->handle, onTrackerFreeNow, t );
    340 }
    341 
    342 /***
    343 ****  UTIL
    344 ***/
     263onStoppedResponse( struct evhttp_request * req UNUSED, void * handle UNUSED )
     264{
     265    dbgmsg( NULL, "got a response to some `stop' message" );
     266}
    345267
    346268static int
     
    421343}
    422344
    423 static tr_tracker_info *
    424 getCurrentAddress( const tr_tracker * t )
    425 {
    426     assert( t->addresses != NULL );
    427     assert( t->addressIndex >= 0 );
    428     assert( t->addressIndex < t->addressCount );
    429 
    430     return t->redirect ? t->redirect
    431                        : t->addresses + t->addressIndex;
    432 }
    433 static int
    434 trackerSupportsScrape( const tr_tracker * t )
    435 {
    436     const tr_tracker_info * info = getCurrentAddress( t );
    437 
    438     return ( info != NULL )
    439         && ( info->scrape != NULL )
    440         && ( info->scrape[0] != '\0' );
    441 }
    442 
    443 
    444 static void
    445 addCommonHeaders( const tr_tracker * t,
    446                   struct evhttp_request * req )
    447 {
    448     char buf[1024];
    449     const tr_tracker_info * address = getCurrentAddress( t );
    450     snprintf( buf, sizeof(buf), "%s:%d", address->address, address->port );
    451     evhttp_add_header( req->output_headers, "Host", buf );
    452     evhttp_add_header( req->output_headers, "Connection", "close" );
    453     evhttp_add_header( req->output_headers, "Content-Length", "0" );
    454     evhttp_add_header( req->output_headers, "User-Agent",
    455                                          TR_NAME "/" LONG_VERSION_STRING );
    456 }
    457 
    458 /**
    459 ***
    460 **/
    461 
    462 struct torrent_hash
    463 {
    464     tr_handle * handle;
    465     uint8_t hash[SHA_DIGEST_LENGTH];
    466 };
    467 
    468 static struct torrent_hash*
    469 torrentHashNew( tr_tracker * t )
    470 {
    471     struct torrent_hash * data = tr_new( struct torrent_hash, 1 );
    472     data->handle = t->handle;
    473     memcpy( data->hash, t->hash, SHA_DIGEST_LENGTH );
    474     return data;
    475 }
    476 
    477 tr_tracker *
    478 findTrackerFromHash( struct torrent_hash * data )
    479 {
    480     tr_torrent * torrent = tr_torrentFindFromHash( data->handle, data->hash );
    481     return torrent ? torrent->tracker : NULL;
    482 }
    483 
    484 /***
    485 ****
    486 ****  SCRAPE
    487 ****
    488 ***/
    489 
    490 static int
    491 onScrapeNow( void * vt );
     345/* Convert to compact form */
     346static uint8_t *
     347parseOldPeers( benc_val_t * bePeers, int * setmePeerCount )
     348{
     349    int i;
     350    uint8_t *compact, *walk;
     351    const int peerCount = bePeers->val.l.count;
     352
     353    assert( bePeers->type == TYPE_LIST );
     354
     355    compact = tr_new( uint8_t, peerCount*6 );
     356
     357    for( i=0, walk=compact; i<peerCount; ++i )
     358    {
     359        struct in_addr addr;
     360        tr_port_t port;
     361        benc_val_t * val;
     362        benc_val_t * peer = &bePeers->val.l.vals[i];
     363
     364        val = tr_bencDictFind( peer, "ip" );
     365        if( !val || val->type!=TYPE_STR || tr_netResolve(val->val.s.s, &addr) )
     366            continue;
     367
     368        memcpy( walk, &addr, 4 );
     369        walk += 4;
     370
     371        val = tr_bencDictFind( peer, "port" );
     372        if( !val || val->type!=TYPE_INT || val->val.i<0 || val->val.i>0xffff )
     373            continue;
     374
     375        port = htons( val->val.i );
     376        memcpy( walk, &port, 2 );
     377        walk += 2;
     378    }
     379
     380    *setmePeerCount = peerCount;
     381    return compact;
     382}
     383
     384static void
     385onTrackerResponse( struct evhttp_request * req, void * torrent_hash )
     386{
     387    const char * warning;
     388    tr_tracker * t;
     389    int responseCode;
     390
     391    t = findTrackerFromHash( torrent_hash );
     392    tr_free( torrent_hash );
     393    if( t == NULL ) /* tracker has been closed */
     394        return;
     395
     396    dbgmsg( t, "got response from tracker: \"%s\"",
     397            ( req && req->response_code_line ) ?  req->response_code_line
     398                                               : "(null)" );
     399
     400    tr_inf( "Torrent \"%s\" tracker response: %s",
     401            t->name,
     402            ( req ? req->response_code_line : "(null)") );
     403
     404    if( req && ( req->response_code == HTTP_OK ) )
     405    {
     406        benc_val_t benc;
     407        const int bencLoaded = !parseBencResponse( req, &benc );
     408
     409        publishErrorClear( t );
     410
     411        if( bencLoaded && benc.type==TYPE_DICT )
     412        {
     413            benc_val_t * tmp;
     414
     415            if(( tmp = tr_bencDictFind( &benc, "failure reason" ))) {
     416                dbgmsg( t, "got failure message [%s]", tmp->val.s.s );
     417                publishErrorMessageAndStop( t, tmp->val.s.s );
     418            }
     419
     420            if(( tmp = tr_bencDictFind( &benc, "warning message" ))) {
     421                dbgmsg( t, "got warning message [%s]", tmp->val.s.s );
     422                publishWarning( t, tmp->val.s.s );
     423            }
     424
     425            if(( tmp = tr_bencDictFind( &benc, "interval" ))) {
     426                dbgmsg( t, "setting interval to %d", tmp->val.i );
     427                t->announceIntervalSec = tmp->val.i;
     428            }
     429
     430            if(( tmp = tr_bencDictFind( &benc, "min interval" ))) {
     431                dbgmsg( t, "setting min interval to %d", tmp->val.i );
     432                t->announceMinIntervalSec = tmp->val.i;
     433            }
     434
     435            if(( tmp = tr_bencDictFind( &benc, "tracker id" )))
     436                t->trackerID = tr_strndup( tmp->val.s.s, tmp->val.s.i );
     437
     438            if(( tmp = tr_bencDictFind( &benc, "complete" )))
     439                t->seederCount = tmp->val.i;
     440
     441            if(( tmp = tr_bencDictFind( &benc, "incomplete" )))
     442                t->leecherCount = tmp->val.i;
     443
     444            if(( tmp = tr_bencDictFind( &benc, "peers" )))
     445            {
     446                int peerCount = 0;
     447                uint8_t * peerCompact = NULL;
     448
     449                if( tmp->type == TYPE_LIST ) /* original protocol */
     450                {
     451                    if( tmp->val.l.count > 0 )
     452                        peerCompact = parseOldPeers( tmp, &peerCount );
     453                }
     454                else if( tmp->type == TYPE_STR ) /* "compact" extension */
     455                {
     456                    if( tmp->val.s.i >= 6 )
     457                    {
     458                        peerCount = tmp->val.s.i / 6;
     459                        peerCompact = tr_new( uint8_t, tmp->val.s.i );
     460                        memcpy( peerCompact, tmp->val.s.s, tmp->val.s.i );
     461                    }
     462                }
     463
     464                publishNewPeers( t, peerCount, peerCompact );
     465                tr_free( peerCompact );
     466            }
     467        }
     468
     469        if( bencLoaded )
     470            tr_bencFree( &benc );
     471    }
     472
     473    if (( warning = updateAddresses( t, req ) )) {
     474        publishWarning( t, warning );
     475        tr_err( warning );
     476    }
     477
     478    /**
     479    ***
     480    **/
     481
     482    responseCode = req ? req->response_code : 503;
     483
     484    if( 200<=responseCode && responseCode<=299 )
     485    {
     486        dbgmsg( t, "request succeeded. reannouncing in %d seconds",
     487                   t->announceIntervalSec );
     488        t->reannounceAt = time(NULL) + t->announceIntervalSec;
     489        t->manualAnnounceAllowedAt = time(NULL) + t->announceMinIntervalSec;
     490    }
     491    else if( 300<=responseCode && responseCode<=399 )
     492    {
     493        dbgmsg( t, "got a redirect; retrying immediately" );
     494
     495        /* it's a redirect... updateAddresses() has already
     496         * parsed the redirect, all that's left is to retry */
     497        t->reannounceAt = time(NULL);
     498        t->manualAnnounceAllowedAt = time(NULL) + t->announceMinIntervalSec;
     499    }
     500    else if( 400<=responseCode && responseCode<=499 )
     501    {
     502        const char * err = req && req->response_code_line
     503            ? req->response_code_line
     504            : "Unspecified 4xx error from tracker.";
     505        dbgmsg( t, err );
     506
     507        /* The request could not be understood by the server due to
     508         * malformed syntax. The client SHOULD NOT repeat the
     509         * request without modifications. */
     510        publishErrorMessageAndStop( t, err );
     511        t->manualAnnounceAllowedAt = ~(time_t)0;
     512        t->reannounceAt = 0;
     513    }
     514    else if( 500<=responseCode && responseCode<=599 )
     515    {
     516        dbgmsg( t, "Got a 5xx error... retrying in 15 seconds." );
     517
     518        /* Response status codes beginning with the digit "5" indicate
     519         * cases in which the server is aware that it has erred or is
     520         * incapable of performing the request.  So we pause a bit and
     521         * try again. */
     522        if( req && req->response_code_line )
     523            publishWarning( t, req->response_code_line );
     524        t->manualAnnounceAllowedAt = ~(time_t)0;
     525        t->reannounceAt = time(NULL) + 15;
     526    }
     527    else
     528    {
     529        dbgmsg( t, "Invalid response from tracker... retrying in 60 seconds." );
     530
     531        /* WTF did we get?? */
     532        if( req && req->response_code_line )
     533            publishWarning( t, req->response_code_line );
     534        t->manualAnnounceAllowedAt = ~(time_t)0;
     535        t->reannounceAt = time(NULL) + 60;
     536    }
     537}
    492538
    493539static void
     
    496542    const char * warning;
    497543    time_t nextScrapeSec = 60;
    498     tr_tracker * t;
    499 
    500     t = findTrackerFromHash( vhash );
     544    tr_tracker * t = findTrackerFromHash( vhash );
     545
     546    dbgmsg( t, "Got scrape response for '%s': %s (%d)", (t ? t->name : "(null)"), (req ? req->response_code_line : "(no line)"), (req ? req->response_code : -1) );
     547
    501548    tr_free( vhash );
    502549    if( t == NULL ) /* tracker's been closed... */
     
    559606    }
    560607
    561     tr_timerFree( &t->scrapeTimer );
    562 
    563     t->scrapeTimer = tr_timerNew( t->handle,
    564                                   onScrapeNow, t,
    565                                   nextScrapeSec*1000 );
    566 }
    567 
    568 static int
    569 onScrapeNow( void * vt )
    570 {
    571     tr_tracker * t = vt;
    572     const tr_tracker_info * address = getCurrentAddress( t );
    573 
    574     if( trackerSupportsScrape( t ) )
    575     {
    576         char * uri;
    577         struct evhttp_connection * evcon;
    578         struct evhttp_request *req;
    579         struct evbuffer * buf = evbuffer_new( );
    580 
    581         evbuffer_add_printf( buf, "%s%sinfo_hash=%s",
    582                              address->scrape,
    583                              ( strchr(address->scrape, '?') == NULL ? "?" : "&" ),
    584                              t->escaped );
    585         uri = tr_strdup( (char*) EVBUFFER_DATA( buf ) );
    586         evbuffer_free( buf );
    587 
    588         tr_inf( "Sending scrape to tracker %s:%d: %s",
    589                 address->address, address->port, uri );
    590 
    591         evcon = getConnection( t, address->address, address->port );
    592         evhttp_connection_set_timeout( evcon, TIMEOUT_INTERVAL_SEC );
    593         req = evhttp_request_new( onScrapeResponse, torrentHashNew( t ) );
    594         addCommonHeaders( t, req );
    595         tr_evhttp_make_request( t->handle, evcon, req, EVHTTP_REQ_GET, uri );
    596     }
    597 
    598     t->scrapeTimer = NULL;
    599     return FALSE;
    600 }
    601 
    602 static void
    603 scrapeNow( tr_tracker * t )
    604 {
    605     onScrapeNow( t );
     608    t->scrapeAt = time(NULL) + nextScrapeSec;
    606609}
    607610
    608611/***
    609612****
    610 ****  TRACKER REQUESTS
    611 ****
    612613***/
     614
     615enum
     616{
     617    TR_REQ_STARTED,
     618    TR_REQ_COMPLETED,
     619    TR_REQ_STOPPED,
     620    TR_REQ_REANNOUNCE,
     621    TR_REQ_SCRAPE,
     622    TR_REQ_COUNT
     623};
     624
     625struct tr_tracker_request
     626{
     627    int port;
     628    int timeout;
     629    int reqtype; /* TR_REQ_* */
     630    char * address;
     631    char * uri;
     632    struct evhttp_request * req;
     633    uint8_t torrent_hash[SHA_DIGEST_LENGTH];
     634};
     635
     636static void
     637freeRequest( struct tr_tracker_request * req )
     638{
     639    tr_free( req->address );
     640    tr_free( req->uri );
     641    tr_free( req );
     642}
     643
     644static void
     645addCommonHeaders( const tr_tracker * t,
     646                  struct evhttp_request * req )
     647{
     648    char buf[1024];
     649    const tr_tracker_info * address = getCurrentAddress( t );
     650    snprintf( buf, sizeof(buf), "%s:%d", address->address, address->port );
     651    evhttp_add_header( req->output_headers, "Host", buf );
     652    evhttp_add_header( req->output_headers, "Connection", "close" );
     653    evhttp_add_header( req->output_headers, "Content-Length", "0" );
     654    evhttp_add_header( req->output_headers, "User-Agent",
     655                                         TR_NAME "/" LONG_VERSION_STRING );
     656}
    613657
    614658static char*
     
    661705}
    662706
    663 /* Convert to compact form */
    664 static uint8_t *
    665 parseOldPeers( benc_val_t * bePeers, int * setmePeerCount )
     707static struct tr_tracker_request*
     708createRequest( tr_handle * handle, const tr_tracker * tracker, int reqtype )
     709{
     710    static const char* strings[TR_REQ_COUNT] = { "started", "completed", "stopped", "", "err" };
     711    const tr_torrent * torrent = tr_torrentFindFromHash( handle, tracker->hash );
     712    const tr_tracker_info * address = getCurrentAddress( tracker );
     713    const int isStopping = reqtype == TR_REQ_STOPPED;
     714    const char * eventName = strings[reqtype];
     715    struct tr_tracker_request * req;
     716
     717    req = tr_new0( struct tr_tracker_request, 1 );
     718    req->address = tr_strdup( address->address );
     719    req->port = address->port;
     720    req->uri = buildTrackerRequestURI( tracker, torrent, eventName );
     721    req->timeout = isStopping ? STOP_TIMEOUT_INTERVAL_SEC : TIMEOUT_INTERVAL_SEC;
     722    req->reqtype = reqtype;
     723    req->req = isStopping
     724        ? evhttp_request_new( onStoppedResponse, handle )
     725        : evhttp_request_new( onTrackerResponse, torrentHashNew(handle, tracker) );
     726    memcpy( req->torrent_hash, tracker->hash, SHA_DIGEST_LENGTH );
     727    addCommonHeaders( tracker, req->req );
     728
     729    return req;
     730}
     731
     732static struct tr_tracker_request*
     733createScrape( tr_handle * handle, const tr_tracker * tracker )
     734{
     735    const tr_tracker_info * a = getCurrentAddress( tracker );
     736    struct tr_tracker_request * req;
     737
     738    req = tr_new0( struct tr_tracker_request, 1 );
     739    req->address = tr_strdup( a->address );
     740    req->port = a->port;
     741    req->timeout = TIMEOUT_INTERVAL_SEC;
     742    req->req = evhttp_request_new( onScrapeResponse, torrentHashNew( handle, tracker ) );
     743    req->reqtype = TR_REQ_SCRAPE;
     744    asprintf( &req->uri, "%s%cinfo_hash=%s", a->scrape, strchr(a->scrape,'?')?'&':'?', tracker->escaped );
     745    memcpy( req->torrent_hash, tracker->hash, SHA_DIGEST_LENGTH );
     746    addCommonHeaders( tracker, req->req );
     747
     748    return req;
     749}
     750
     751struct tr_tracker_handle
     752{
     753    int socketCount;
     754    tr_timer * pulseTimer;
     755    tr_list * requestQueue;
     756    tr_list * scrapeQueue;
     757};
     758
     759static int pulse( void * vhandle );
     760
     761static void
     762ensureGlobalsExist( tr_handle * handle )
     763{
     764    if( handle->tracker == NULL )
     765    {
     766        handle->tracker = tr_new0( struct tr_tracker_handle, 1 );
     767        handle->tracker->pulseTimer = tr_timerNew( handle, pulse, handle, PULSE_INTERVAL_MSEC );
     768        dbgmsg( NULL, "creating tracker timer" );
     769    }
     770}
     771
     772static void
     773freeRequest2( void * req )
     774{
     775    freeRequest( req );
     776}
     777
     778void
     779tr_trackerShuttingDown( tr_handle * handle )
     780{
     781    /* since we're shutting down, we don't need to scrape anymore... */
     782    if( handle->tracker )
     783        tr_list_free( &handle->tracker->scrapeQueue, freeRequest2 );
     784}
     785
     786static int
     787maybeFreeGlobals( tr_handle * handle )
     788{
     789    int globalsExist = handle->tracker != NULL;
     790
     791    if( globalsExist
     792        && ( handle->tracker->socketCount < 1 )
     793        && ( handle->tracker->requestQueue == NULL )
     794        && ( handle->tracker->scrapeQueue == NULL )
     795        && ( handle->torrentList== NULL ) )
     796    {
     797        dbgmsg( NULL, "freeing tracker timer" );
     798        tr_timerFree( &handle->tracker->pulseTimer );
     799        tr_free( handle->tracker );
     800        handle->tracker = NULL;
     801        globalsExist = FALSE;
     802    }
     803
     804    return globalsExist;
     805}
     806
     807/***
     808****
     809***/
     810
     811static int
     812freeConnection( void * evcon )
     813{
     814    evhttp_connection_free( evcon );
     815    return FALSE;
     816}
     817static void
     818connectionClosedCB( struct evhttp_connection * evcon, void * vhandle )
     819{
     820    tr_handle * handle = vhandle;
     821
     822    assert( handle );
     823    assert( handle->tracker );
     824
     825    /* libevent references evcon right after calling this function,
     826       so we can't free it yet... defer it to after this call chain
     827       has played out */
     828    tr_timerNew( handle, freeConnection, evcon, 100 );
     829
     830    --handle->tracker->socketCount;
     831    dbgmsg( NULL, "decrementing socket count to %d", handle->tracker->socketCount );
     832    pulse( handle );
     833}
     834
     835static struct evhttp_connection*
     836getConnection( tr_handle * handle, const char * address, int port )
     837{
     838    struct evhttp_connection * c = evhttp_connection_new( address, port );
     839    evhttp_connection_set_closecb( c, connectionClosedCB, handle );
     840    return c;
     841}
     842
     843static void
     844invokeRequest( tr_handle * handle, const struct tr_tracker_request * req )
     845{
     846    struct evhttp_connection * evcon = getConnection( handle, req->address, req->port );
     847    tr_tracker * t = findTracker( handle, req->torrent_hash );
     848    dbgmsg( t, "sending '%s' to tracker %s:%d, timeout is %d", req->uri, req->address, req->port, (int)req->timeout );
     849    evhttp_connection_set_timeout( evcon, req->timeout );
     850    if( evhttp_make_request( evcon, req->req, EVHTTP_REQ_GET, req->uri ))
     851        publishErrorMessageAndStop( t, "Tracker could not be reached." );
     852    else {
     853        ++handle->tracker->socketCount;
     854        dbgmsg( t, "incremented socket count to %d", handle->tracker->socketCount );
     855    }
     856}
     857
     858static void
     859invokeNextInQueue( tr_handle * handle, tr_list ** list )
     860{
     861    struct tr_tracker_request * req = tr_list_pop_front( list );
     862    invokeRequest( handle, req );
     863    freeRequest( req );
     864}
     865
     866static int
     867socketIsAvailable( tr_handle * handle )
     868{
     869    return handle->tracker->socketCount < MAX_TRACKER_SOCKETS;
     870}
     871
     872static void ensureGlobalsExist( tr_handle * );
     873
     874static void
     875enqueueScrape( tr_handle * handle, const tr_tracker * tracker )
     876{
     877    struct tr_tracker_request * req;
     878    ensureGlobalsExist( handle );
     879    req = createScrape( handle, tracker );
     880    tr_list_append( &handle->tracker->scrapeQueue, req );
     881}
     882
     883static void
     884enqueueRequest( tr_handle * handle, const tr_tracker * tracker, int reqtype )
     885{
     886    struct tr_tracker_request * req;
     887    ensureGlobalsExist( handle );
     888    req = createRequest( handle, tracker, reqtype );
     889    tr_list_append( &handle->tracker->requestQueue, req );
     890}
     891
     892static int
     893pulse( void * vhandle )
     894{
     895    tr_handle * handle = vhandle;
     896    struct tr_tracker_handle * th = handle->tracker;
     897    tr_torrent * tor;
     898    const time_t now = time( NULL );
     899
     900    if( handle->tracker->socketCount || tr_list_size(th->requestQueue) || tr_list_size(th->scrapeQueue) )
     901        dbgmsg( NULL, "tracker pulse... %d sockets, %d reqs left, %d scrapes left", handle->tracker->socketCount, tr_list_size(th->requestQueue), tr_list_size(th->scrapeQueue) );
     902
     903    /* upkeep: queue periodic rescrape / reannounce */
     904    for( tor=handle->torrentList; tor; tor=tor->next )
     905    {
     906        tr_tracker * t = tor->tracker;
     907
     908        if( t->scrapeAt && trackerSupportsScrape( t ) && ( now >= t->scrapeAt ) ) {
     909            t->scrapeAt = 0;
     910            enqueueScrape( handle, t );
     911        }
     912
     913        if( t->reannounceAt && t->isRunning && ( now >= t->reannounceAt ) ) {
     914            t->reannounceAt = 0;
     915            enqueueRequest( handle, t, TR_REQ_REANNOUNCE );
     916        }
     917    }
     918
     919    if( handle->tracker->socketCount || tr_list_size(th->requestQueue) || tr_list_size(th->scrapeQueue) )
     920        dbgmsg( NULL, "tracker pulse after upkeep... %d sockets, %d reqs left, %d scrapes left", handle->tracker->socketCount, tr_list_size(th->requestQueue), tr_list_size(th->scrapeQueue) );
     921
     922    /* look for things to do... process all the requests, then process all the scrapes */
     923    while( th->requestQueue && socketIsAvailable( handle ) )
     924        invokeNextInQueue( handle, &th->requestQueue );
     925    while( th->scrapeQueue && socketIsAvailable( handle ) )
     926        invokeNextInQueue( handle, &th->scrapeQueue );
     927
     928    if( handle->tracker->socketCount || tr_list_size(th->requestQueue) || tr_list_size(th->scrapeQueue) )
     929        dbgmsg( NULL, "tracker pulse done... %d sockets, %d reqs left, %d scrapes left", handle->tracker->socketCount, tr_list_size(th->requestQueue), tr_list_size(th->scrapeQueue) );
     930
     931    return maybeFreeGlobals( handle );
     932}
     933
     934/***
     935****  LIFE CYCLE
     936***/
     937
     938static void
     939generateKeyParam( char * msg, int len )
    666940{
    667941    int i;
    668     uint8_t *compact, *walk;
    669     const int peerCount = bePeers->val.l.count;
    670 
    671     assert( bePeers->type == TYPE_LIST );
    672 
    673     compact = tr_new( uint8_t, peerCount*6 );
    674 
    675     for( i=0, walk=compact; i<peerCount; ++i )
    676     {
    677         struct in_addr addr;
    678         tr_port_t port;
    679         benc_val_t * val;
    680         benc_val_t * peer = &bePeers->val.l.vals[i];
    681 
    682         val = tr_bencDictFind( peer, "ip" );
    683         if( !val || val->type!=TYPE_STR || tr_netResolve(val->val.s.s, &addr) )
    684             continue;
    685 
    686         memcpy( walk, &addr, 4 );
    687         walk += 4;
    688 
    689         val = tr_bencDictFind( peer, "port" );
    690         if( !val || val->type!=TYPE_INT || val->val.i<0 || val->val.i>0xffff )
    691             continue;
    692 
    693         port = htons( val->val.i );
    694         memcpy( walk, &port, 2 );
    695         walk += 2;
    696     }
    697 
    698     *setmePeerCount = peerCount;
    699     return compact;
    700 }
    701 
    702 static int onRetry( void * vt );
    703 static int onReannounce( void * vt );
    704 
    705 static void
    706 onStoppedResponse( struct evhttp_request * req UNUSED, void * handle UNUSED )
    707 {
    708     dbgmsg( NULL, "got a response to some `stop' message" );
    709 }
    710 
    711 static void
    712 onTrackerResponse( struct evhttp_request * req, void * torrent_hash )
    713 {
    714     const char * warning;
     942    const char * pool = "abcdefghijklmnopqrstuvwxyz0123456789";
     943    const int poolSize = strlen( pool );
     944    for( i=0; i<len; ++i )
     945        *msg++ = pool[tr_rand(poolSize)];
     946    *msg = '\0';
     947}
     948
     949static void
     950escape( char * out, const uint8_t * in, int in_len ) /* rfc2396 */
     951{
     952    const uint8_t *end = in + in_len;
     953    while( in != end )
     954        if( isalnum(*in) )
     955            *out++ = (char) *in++;
     956        else
     957            out += snprintf( out, 4, "%%%02X", (unsigned int)*in++ );
     958    *out = '\0';
     959}
     960
     961tr_tracker *
     962tr_trackerNew( const tr_torrent * torrent )
     963{
     964    const tr_info * info = &torrent->info;
     965    int i, j, sum, *iwalk;
     966    tr_tracker_info * nwalk;
    715967    tr_tracker * t;
    716     int responseCode;
    717 
    718     t = findTrackerFromHash( torrent_hash );
    719     tr_free( torrent_hash );
    720     if( t == NULL ) /* tracker has been closed */
    721         return;
    722 
    723     dbgmsg( t, "got response from tracker: \"%s\"",
    724             ( req && req->response_code_line ) ?  req->response_code_line
    725                                                : "(null)" );
    726 
    727     tr_inf( "Torrent \"%s\" tracker response: %s",
    728             t->name,
    729             ( req ? req->response_code_line : "(null)") );
    730 
    731     if( req && ( req->response_code == HTTP_OK ) )
    732     {
    733         benc_val_t benc;
    734         const int bencLoaded = !parseBencResponse( req, &benc );
    735 
    736         publishErrorClear( t );
    737 
    738         if( bencLoaded && benc.type==TYPE_DICT )
     968
     969    tr_dbg( "making a new tracker for \"%s\"", info->primaryAddress );
     970
     971    t = tr_new0( tr_tracker, 1 );
     972    t->handle = torrent->handle;
     973    t->scrapeIntervalSec       = DEFAULT_SCRAPE_INTERVAL_SEC;
     974    t->announceIntervalSec     = DEFAULT_ANNOUNCE_INTERVAL_SEC;
     975    t->announceMinIntervalSec  = DEFAULT_ANNOUNCE_MIN_INTERVAL_SEC;
     976    generateKeyParam( t->key_param, KEYLEN );
     977
     978    t->publisher = tr_publisherNew( );
     979    t->timesDownloaded = -1;
     980    t->seederCount = -1;
     981    t->leecherCount = -1;
     982    t->manualAnnounceAllowedAt = ~(time_t)0;
     983    t->name = tr_strdup( info->name );
     984    memcpy( t->hash, info->hash, SHA_DIGEST_LENGTH );
     985    escape( t->escaped, info->hash, SHA_DIGEST_LENGTH );
     986
     987    for( sum=i=0; i<info->trackerTiers; ++i )
     988         sum += info->trackerList[i].count;
     989    t->addresses = nwalk = tr_new0( tr_tracker_info, sum );
     990    t->addressIndex = 0;
     991    t->addressCount = sum;
     992    t->tierFronts = iwalk = tr_new0( int, sum );
     993
     994    for( i=0; i<info->trackerTiers; ++i )
     995    {
     996        const int tierFront = nwalk - t->addresses;
     997
     998        for( j=0; j<info->trackerList[i].count; ++j )
    739999        {
    740             benc_val_t * tmp;
    741 
    742             if(( tmp = tr_bencDictFind( &benc, "failure reason" ))) {
    743                 dbgmsg( t, "got failure message [%s]", tmp->val.s.s );
    744                 publishErrorMessage( t, tmp->val.s.s );
    745             }
    746 
    747             if(( tmp = tr_bencDictFind( &benc, "warning message" ))) {
    748                 dbgmsg( t, "got warning message [%s]", tmp->val.s.s );
    749                 publishWarning( t, tmp->val.s.s );
    750             }
    751 
    752             if(( tmp = tr_bencDictFind( &benc, "interval" ))) {
    753                 dbgmsg( t, "setting interval to %d", tmp->val.i );
    754                 t->announceIntervalSec = tmp->val.i;
    755             }
    756 
    757             if(( tmp = tr_bencDictFind( &benc, "min interval" ))) {
    758                 dbgmsg( t, "setting min interval to %d", tmp->val.i );
    759                 t->announceMinIntervalSec = tmp->val.i;
    760             }
    761 
    762             if(( tmp = tr_bencDictFind( &benc, "tracker id" )))
    763                 t->trackerID = tr_strndup( tmp->val.s.s, tmp->val.s.i );
    764 
    765             if(( tmp = tr_bencDictFind( &benc, "complete" )))
    766                 t->seederCount = tmp->val.i;
    767 
    768             if(( tmp = tr_bencDictFind( &benc, "incomplete" )))
    769                 t->leecherCount = tmp->val.i;
    770 
    771             if(( tmp = tr_bencDictFind( &benc, "peers" )))
    772             {
    773                 int peerCount = 0;
    774                 uint8_t * peerCompact = NULL;
    775 
    776                 if( tmp->type == TYPE_LIST ) /* original protocol */
    777                 {
    778                     if( tmp->val.l.count > 0 )
    779                         peerCompact = parseOldPeers( tmp, &peerCount );
    780                 }
    781                 else if( tmp->type == TYPE_STR ) /* "compact" extension */
    782                 {
    783                     if( tmp->val.s.i >= 6 )
    784                     {
    785                         peerCount = tmp->val.s.i / 6;
    786                         peerCompact = tr_new( uint8_t, tmp->val.s.i );
    787                         memcpy( peerCompact, tmp->val.s.s, tmp->val.s.i );
    788                     }
    789                 }
    790 
    791                 publishNewPeers( t, peerCount, peerCompact );
    792                 tr_free( peerCompact );
    793             }
     1000            const tr_tracker_info * src = &info->trackerList[i].list[j];
     1001            nwalk->address = tr_strdup( src->address );
     1002            nwalk->port = src->port;
     1003            nwalk->announce = tr_strdup( src->announce );
     1004            nwalk->scrape = tr_strdup( src->scrape );
     1005            ++nwalk;
     1006
     1007            *iwalk++ = tierFront;
    7941008        }
    795 
    796         if( bencLoaded )
    797             tr_bencFree( &benc );
    798     }
    799 
    800     if (( warning = updateAddresses( t, req ) )) {
    801         publishWarning( t, warning );
    802         tr_err( warning );
    803     }
    804 
    805     /**
    806     ***
    807     **/
    808 
    809     responseCode = req ? req->response_code : 503;
    810 
    811     if( 200<=responseCode && responseCode<=299 )
    812     {
    813         dbgmsg( t, "request succeeded. reannouncing in %d seconds",
    814                    t->announceIntervalSec );
    815         t->manualAnnounceAllowedAt = time(NULL)
    816                                    + t->announceMinIntervalSec;
    817         t->reannounceTimer = tr_timerNew( t->handle,
    818                                           onReannounce, t,
    819                                           t->announceIntervalSec * 1000 );
    820     }
    821     else if( 300<=responseCode && responseCode<=399 )
    822     {
    823         dbgmsg( t, "got a redirect; retrying immediately" );
    824 
    825         /* it's a redirect... updateAddresses() has already
    826          * parsed the redirect, all that's left is to retry */
    827         onRetry( t );
    828     }
    829     else if( 400<=responseCode && responseCode<=499 )
    830     {
    831         dbgmsg( t, "got a 4xx error." );
    832 
    833         /* The request could not be understood by the server due to
    834          * malformed syntax. The client SHOULD NOT repeat the
    835          * request without modifications. */
    836         if( req && req->response_code_line )
    837             publishErrorMessage( t, req->response_code_line );
    838         t->manualAnnounceAllowedAt = ~(time_t)0;
    839         t->reannounceTimer = NULL;
    840     }
    841     else if( 500<=responseCode && responseCode<=599 )
    842     {
    843         dbgmsg( t, "Got a 5xx error... retrying in 15 seconds." );
    844 
    845         /* Response status codes beginning with the digit "5" indicate
    846          * cases in which the server is aware that it has erred or is
    847          * incapable of performing the request.  So we pause a bit and
    848          * try again. */
    849         if( req && req->response_code_line )
    850             publishWarning( t, req->response_code_line );
    851         t->manualAnnounceAllowedAt = ~(time_t)0;
    852         t->reannounceTimer = tr_timerNew( t->handle, onRetry, t, 15 * 1000 );
    853     }
    854     else
    855     {
    856         dbgmsg( t, "Invalid response from tracker... retrying in 120 seconds." );
    857 
    858         /* WTF did we get?? */
    859         if( req && req->response_code_line )
    860             publishErrorMessage( t, req->response_code_line );
    861         t->manualAnnounceAllowedAt = ~(time_t)0;
    862         t->reannounceTimer = tr_timerNew( t->handle, onRetry, t, 120 * 1000 );
    863     }
    864 }
    865 
    866 static int
    867 sendTrackerRequest( void * vt, const char * eventName )
    868 {
     1009    }
     1010
     1011    assert( nwalk - t->addresses == sum );
     1012    assert( iwalk - t->tierFronts == sum );
     1013
     1014    if( trackerSupportsScrape( t ) )
     1015        enqueueScrape( t->handle, t );
     1016
     1017    return t;
     1018}
     1019
     1020static void
     1021onTrackerFreeNow( void * vt )
     1022{
     1023    int i;
    8691024    tr_tracker * t = vt;
    870     const int isStopping = eventName && !strcmp( eventName, "stopped" );
    871     const tr_tracker_info * address = getCurrentAddress( t );
    872     char * uri;
    873     struct evhttp_connection * evcon;
    874     const tr_torrent * tor;
    875 
    876     tor = tr_torrentFindFromHash( t->handle, t->hash );
    877     if( tor == NULL )
    878         return FALSE;   
    879 
    880     uri = buildTrackerRequestURI( t, tor, eventName );
    881 
    882     tr_inf( "Torrent \"%s\" sending '%s' to tracker %s:%d: %s",
    883             t->name,
    884             (eventName ? eventName : "periodic announce"),
    885             address->address, address->port,
    886             uri );
    887 
    888     /* kill any pending requests */
    889     dbgmsg( t, "clearing announce timer" );
    890     tr_timerFree( &t->reannounceTimer );
    891 
    892     evcon = getConnection( t, address->address, address->port );
    893     if ( !evcon ) {
    894         tr_err( "Can't make a connection to %s:%d", address->address, address->port );
    895         tr_free( uri );
    896     } else {
    897         struct evhttp_request * req;
    898         if( eventName != t->lastRequest ) {
    899             tr_free( t->lastRequest );
    900             t->lastRequest = tr_strdup( eventName );
    901         }
    902         if( isStopping ) {
    903             evhttp_connection_set_timeout( evcon, STOP_TIMEOUT_INTERVAL_SEC );
    904             req = evhttp_request_new( onStoppedResponse, t->handle );
    905         } else {
    906             evhttp_connection_set_timeout( evcon, TIMEOUT_INTERVAL_SEC );
    907             req = evhttp_request_new( onTrackerResponse, torrentHashNew(t) );
    908         }
    909         dbgmsg( t, "sending \"%s\" request to tracker", eventName ? eventName : "reannounce" );
    910 
    911         addCommonHeaders( t, req );
    912         tr_evhttp_make_request( t->handle, evcon,
    913                                 req, EVHTTP_REQ_GET, uri );
    914     }
    915 
    916     return FALSE;
    917 }
    918 
    919 static int
    920 onReannounce( void * vt )
    921 {
    922     tr_tracker * t = vt;
    923     dbgmsg( t, "onReannounce" );
    924     sendTrackerRequest( t, "" );
    925     dbgmsg( t, "onReannounce setting announceTimer to NULL" );
    926     t->reannounceTimer = NULL;
    927     return FALSE;
    928 }
    929 
    930 static int
    931 onRetry( void * vt )
    932 {
    933     tr_tracker * t = vt;
    934     dbgmsg( t, "onRetry" );
    935     sendTrackerRequest( t, t->lastRequest );
    936     dbgmsg( t, "onRetry setting announceTimer to NULL" );
    937     t->reannounceTimer = NULL;
    938     return FALSE;
    939 }
     1025
     1026    tr_publisherFree( &t->publisher );
     1027    tr_free( t->name );
     1028    tr_free( t->trackerID );
     1029
     1030    /* addresses... */
     1031    for( i=0; i<t->addressCount; ++i )
     1032        tr_trackerInfoClear( &t->addresses[i] );
     1033    tr_free( t->addresses );
     1034    tr_free( t->tierFronts );
     1035
     1036    /* redirect... */
     1037    if( t->redirect ) {
     1038        tr_trackerInfoClear( t->redirect );
     1039        tr_free( t->redirect );
     1040    }
     1041
     1042    tr_free( t );
     1043}
     1044
     1045void
     1046tr_trackerFree( tr_tracker * t )
     1047{
     1048    tr_runInEventThread( t->handle, onTrackerFreeNow, t );
     1049}
     1050
    9401051
    9411052/***
     
    9871098}
    9881099
    989 struct request_data
    990 {
    991     tr_tracker * t;
    992     const char * command;
    993 };
    994 
    995 static void
    996 sendRequestFromEventThreadImpl( void * vdata )
    997 {
    998     struct request_data * data = vdata;
    999     sendTrackerRequest( data->t, data->command );
    1000     tr_free( data );
    1001 }
    1002 
    1003 static void
    1004 sendRequestFromEventThread( tr_tracker * t, const char * command )
    1005 {
    1006     struct request_data * data = tr_new( struct request_data, 1 );
    1007     data->t = t;
    1008     data->command = command;
    1009     tr_runInEventThread( t->handle, sendRequestFromEventThreadImpl, data );
    1010 }
    10111100
    10121101void
     
    10141103{
    10151104    tr_peerIdNew( t->peer_id, sizeof(t->peer_id) );
    1016 
    1017     if( !t->reannounceTimer && !t->isRunning )
    1018     {
     1105    if( t->isRunning == 0 ) {
    10191106        t->isRunning = 1;
    1020         sendRequestFromEventThread( t, "started" );
     1107        enqueueRequest( t->handle, t, TR_REQ_STARTED );
    10211108    }
    10221109}
     
    10251112tr_trackerReannounce( tr_tracker * t )
    10261113{
    1027     sendRequestFromEventThread( t, "started" );
     1114    enqueueRequest( t->handle, t, TR_REQ_REANNOUNCE );
    10281115}
    10291116
     
    10311118tr_trackerCompleted( tr_tracker * t )
    10321119{
    1033     sendRequestFromEventThread( t, "completed" );
     1120    enqueueRequest( t->handle, t, TR_REQ_COMPLETED );
    10341121}
    10351122
     
    10371124tr_trackerStop( tr_tracker * t )
    10381125{
    1039     if( t->isRunning )
    1040     {
     1126    if( t->isRunning ) {
    10411127        t->isRunning = 0;
    1042         sendRequestFromEventThread( t, "stopped" );
     1128        enqueueRequest( t->handle, t, TR_REQ_STOPPED );
    10431129    }
    10441130}
  • branches/0.9x/libtransmission/tracker.h

    r3451 r4037  
    2727
    2828void  tr_trackerFree ( tr_tracker * );
     29
     30void  tr_trackerShuttingDown( tr_handle * );
    2931
    3032/**
  • branches/0.9x/libtransmission/transmission.c

    r3927 r4037  
    4242#include "ratecontrol.h"
    4343#include "shared.h"
     44#include "tracker.h"
    4445#include "trevent.h"
    4546#include "utils.h"
     
    321322    tr_torrent * t;
    322323
     324    tr_trackerShuttingDown( h );
     325
    323326    for( t=h->torrentList; t!=NULL; t=t->next )
    324327        tr_torrentClose( t );
     
    345348}
    346349
     350#define SHUTDOWN_MAX_SECONDS 30
     351
    347352void
    348353tr_close( tr_handle * h )
    349354{
    350     const int maxwait_msec = 6 * 1000;
     355    const int maxwait_msec = SHUTDOWN_MAX_SECONDS * 1000;
    351356    const uint64_t deadline = tr_date( ) + maxwait_msec;
    352357
     
    362367    free( h->tag );
    363368    free( h );
    364 }
    365 
    366 void
    367 tr_getSessionStats( const tr_handle   * handle,
    368                     tr_session_stats  * setme )
    369 {
    370     assert( handle != NULL );
    371     assert( setme != NULL );
    372 
    373     /* FIXME */
    374     setme->downloadedGigs   = 4;
    375     setme->downloadedBytes  = 8;
    376     setme->uploadedGigs     = 15;
    377     setme->uploadedBytes    = 16;
    378     setme->ratio            = 23;
    379     setme->filesAdded       = 42;
    380     setme->sessionCount     = 666;
    381     setme->secondsActive    = 2112;
    382 }
    383 
    384 void
    385 tr_getCumulativeSessionStats( const tr_handle   * handle,
    386                               tr_session_stats  * setme )
    387 {
    388     tr_getSessionStats( handle, setme );
    389369}
    390370
  • branches/0.9x/libtransmission/transmission.h

    r3925 r4037  
    6161#endif
    6262
    63 #define TR_DEFAULT_PORT 51413
     63#define TR_DEFAULT_PORT   51413
    6464
    6565enum
     
    119119typedef struct tr_session_stats
    120120{
    121     uint64_t downloadedGigs;  /* total down / GiB */
    122     uint64_t downloadedBytes; /* total down % GiB */
    123     uint64_t uploadedGigs;    /* total up / GiB */
    124     uint64_t uploadedBytes;   /* total up % GiB */
     121    uint64_t uploadedBytes;   /* total up */
     122    uint64_t downloadedBytes; /* total down */
    125123    double ratio;             /* total up / total down */
    126124    uint64_t filesAdded;      /* number of files added */
     
    716714typedef enum
    717715{
    718     TR_PEER_STATUS_HANDSHAKE,
    719     TR_PEER_STATUS_PEER_IS_CHOKED,
    720     TR_PEER_STATUS_CLIENT_IS_CHOKED,
    721     TR_PEER_STATUS_CLIENT_IS_INTERESTED,
    722     TR_PEER_STATUS_READY,
    723     TR_PEER_STATUS_ACTIVE_AND_CHOKED,
    724     TR_PEER_STATUS_REQUEST_SENT,
    725     TR_PEER_STATUS_ACTIVE
     716    TR_PEER_STATUS_HANDSHAKE             = (1<<0), /* we're handshaking with peer */
     717
     718    TR_PEER_STATUS_PEER_IS_SENDING       = (1<<1), /* peer is sending data to us */
     719    TR_PEER_STATUS_PEER_IS_INTERESTED    = (1<<2), /* we have data the peer wants */
     720    TR_PEER_STATUS_PEER_IS_CHOKED        = (1<<3), /* we refuse to send data to the peer */
     721
     722    TR_PEER_STATUS_CLIENT_IS_SENDING     = (1<<4), /* we're sending data to the peer */
     723    TR_PEER_STATUS_CLIENT_SENT_REQUEST   = (1<<5), /* we've sent the peer a request */
     724    TR_PEER_STATUS_CLIENT_IS_INTERESTED  = (1<<6), /* peer has data that we want */
     725    TR_PEER_STATUS_CLIENT_IS_CHOKED      = (1<<7), /* peer refuses to send data to us */
    726726}
    727727tr_peer_status;
Note: See TracChangeset for help on using the changeset viewer.