Changeset 1242


Ignore:
Timestamp:
Dec 17, 2006, 4:36:27 PM (15 years ago)
Author:
livings124
Message:

merge multitracker branch:

Adds announce list, support for code 301/302 redirect, improvements to mac inspector, better queueing with errors, random extra wait of up to a minute between announces, and other changes.

Location:
trunk
Files:
17 edited

Legend:

Unmodified
Added
Removed
  • trunk/Transmission.xcodeproj/project.pbxproj

    r1149 r1242  
    10821082                                GCC_VERSION_ppc = 4.0;
    10831083                                GCC_WARN_ABOUT_RETURN_TYPE = NO;
     1084                                GCC_WARN_SIGN_COMPARE = NO;
    10841085                                MACOSX_DEPLOYMENT_TARGET_i386 = 10.4;
    10851086                                MACOSX_DEPLOYMENT_TARGET_ppc = 10.4;
  • trunk/cli/transmissioncli.c

    r1152 r1242  
    122122        tr_info_t * info = tr_torrentInfo( tor );
    123123
     124        s = tr_torrentStat( tor );
     125
    124126        /* Print torrent info (quite à la btshowmetainfo) */
    125127        printf( "hash:     " );
     
    130132        printf( "\n" );
    131133        printf( "tracker:  %s:%d\n",
    132                 info->trackerAddress, info->trackerPort );
    133         printf( "announce: %s\n", info->trackerAnnounce );
     134                s->trackerAddress, s->trackerPort );
     135        printf( "announce: %s\n", s->trackerAnnounce );
    134136        printf( "size:     %"PRIu64" (%"PRIu64" * %d + %"PRIu64")\n",
    135137                info->totalSize, info->totalSize / info->pieceSize,
  • trunk/gtk/dialogs.c

    r1150 r1242  
    573573  INFOSEP(table, ii);
    574574
    575   if(80 == in->trackerPort)
     575  if(80 == sb->trackerPort)
    576576    INFOLINEA(table, ii, _("Tracker:"), g_strdup_printf("http://%s",
    577               in->trackerAddress));
     577              sb->trackerAddress));
    578578  else
    579579    INFOLINEA(table, ii, _("Tracker:"), g_strdup_printf("http://%s:%i",
    580               in->trackerAddress, in->trackerPort));
    581   INFOLINE(table, ii, _("Announce:"), in->trackerAnnounce);
     580              sb->trackerAddress, sb->trackerPort));
     581  INFOLINE(table, ii, _("Announce:"), sb->trackerAnnounce);
    582582  INFOLINEA(table, ii, _("Piece Size:"), readablesize(in->pieceSize));
    583583  INFOLINEF(table, ii, "%i", _("Pieces:"), in->pieceCount);
  • trunk/libtransmission/http.h

    r920 r1242  
    3737int         tr_httpResponseCode( const char * data, int len );
    3838
    39 #define TR_HTTP_STATUS_OK( st )       ( 200 <= (st) && 299 >= (st) )
    40 #define TR_HTTP_STATUS_REDIRECT( st ) ( 300 <= (st) && 399 >= (st) )
    41 #define TR_HTTP_STATUS_FAIL( st )     ( 400 <= (st) && 599 >= (st) )
     39#define TR_HTTP_STATUS_OK( st )             ( 200 <= (st) && 299 >= (st) )
     40#define TR_HTTP_STATUS_REDIRECT( st )       ( 300 <= (st) && 399 >= (st) )
     41#define TR_HTTP_STATUS_FAIL( st )           ( 400 <= (st) && 599 >= (st) )
     42#define TR_HTTP_STATUS_FAIL_CLIENT( st )    ( 400 <= (st) && 499 >= (st) )
     43#define TR_HTTP_STATUS_FAIL_SERVER( st )    ( 500 <= (st) && 599 >= (st) )
    4244
    4345/*
  • trunk/libtransmission/internal.h

    r1149 r1242  
    161161    char              escapedHashString[3*SHA_DIGEST_LENGTH+1];
    162162
    163     char              scrape[MAX_PATH_LENGTH];
    164 
    165163    /* Where to download */
    166164    char            * destination;
  • trunk/libtransmission/metainfo.c

    r1124 r1242  
    3030 * Local prototypes
    3131 **********************************************************************/
     32static int getannounce( tr_info_t * inf, benc_val_t * meta );
    3233#define strcatUTF8( dst, src) _strcatUTF8( (dst), sizeof( dst ) - 1, (src) )
    3334static void _strcatUTF8( char *, int, char * );
     
    4445    char       * buf;
    4546    benc_val_t   meta, * beInfo, * list, * val;
    46     char * s, * s2, * s3;
    4747    int          i;
    48     struct stat sb;
     48    struct stat  sb;
    4949
    5050    assert( NULL == path || NULL == savedHash );
     
    5252    assert( NULL == savedHash || !saveCopy );
    5353
    54     if ( NULL != savedHash )
     54    if( NULL != savedHash )
    5555    {
    5656        snprintf( inf->torrent, MAX_PATH_LENGTH, "%s/%s",
     
    148148    /* We won't need this anymore */
    149149    free( buf );
    150 
    151     if( !( val = tr_bencDictFind( &meta, "announce" ) ) )
    152     {
    153         tr_err( "No \"announce\" entry" );
    154         tr_bencFree( &meta );
    155         return 1;
    156     }
    157    
    158     /* Skip spaces */
    159     s3 = val->val.s.s;
    160     while( *s3 && *s3 == ' ' )
    161     {
    162         s3++;
    163     }
    164 
    165     /* Parse announce URL */
    166     if( strncmp( s3, "http://", 7 ) )
    167     {
    168         tr_err( "Invalid announce URL (%s)", inf->trackerAddress );
    169         tr_bencFree( &meta );
    170         return 1;
    171     }
    172     s  = strchr( s3 + 7, ':' );
    173     s2 = strchr( s3 + 7, '/' );
    174     if( s && s < s2 )
    175     {
    176         memcpy( inf->trackerAddress, s3 + 7,
    177                 (long) s - (long) s3 - 7 );
    178         inf->trackerPort = atoi( s + 1 );
    179     }
    180     else if( s2 )
    181     {
    182         memcpy( inf->trackerAddress, s3 + 7,
    183                 (long) s2 - (long) s3 - 7 );
    184         inf->trackerPort = 80;
    185     }
    186     else
    187     {
    188         tr_err( "Invalid announce URL (%s)", inf->trackerAddress );
    189         tr_bencFree( &meta );
    190         return 1;
    191     }
    192     snprintf( inf->trackerAnnounce, MAX_PATH_LENGTH, "%s", s2 );
    193    
     150       
    194151    /* Comment info */
    195152    if( ( val = tr_bencDictFind( &meta, "comment.utf-8" ) ) || ( val = tr_bencDictFind( &meta, "comment" ) ) )
     
    213170        inf->dateCreated = 0;
    214171    }
    215 
     172   
     173    /* Private torrent */
     174    if( ( val = tr_bencDictFind( beInfo, "private" ) ) && TYPE_INT == val->type )
     175    {
     176        inf->privateTorrent = val->val.i;
     177    }
     178   
    216179    /* Piece length */
    217180    if( !( val = tr_bencDictFind( beInfo, "piece length" ) ) )
     
    298261    {
    299262        tr_err( "Size of hashes and files don't match" );
    300         free( inf->pieces );
     263        tr_metainfoFree( inf );
    301264        tr_bencFree( &meta );
    302265        return 1;
    303266    }
    304267
     268    /* get announce or announce-list */
     269    if( getannounce( inf, &meta ) )
     270    {
     271        tr_metainfoFree( inf );
     272        tr_bencFree( &meta );
     273        return 1;
     274    }
     275
    305276    tr_bencFree( &meta );
     277    return 0;
     278}
     279
     280void tr_metainfoFree( tr_info_t * inf )
     281{
     282    int ii, jj;
     283
     284    free( inf->pieces );
     285    free( inf->files );
     286   
     287    for( ii = 0; ii < inf->trackerTiers; ii++ )
     288    {
     289        for( jj = 0; jj < inf->trackerList[ii].count; jj++ )
     290        {
     291            free( inf->trackerList[ii].list[jj].address );
     292            free( inf->trackerList[ii].list[jj].announce );
     293        }
     294        free( inf->trackerList[ii].list );
     295    }
     296    free( inf->trackerList );
     297}
     298
     299static int getannounce( tr_info_t * inf, benc_val_t * meta )
     300{
     301    benc_val_t              * val, * subval, * urlval;
     302    char                    * address, * announce;
     303    int                       ii, jj, port, random;
     304    tr_tracker_info_t * sublist;
     305    int subcount;
     306    void * swapping;
     307
     308    /* Announce-list */
     309    val = tr_bencDictFind( meta, "announce-list" );
     310    if( NULL != val && TYPE_LIST == val->type && 0 < val->val.l.count )
     311    {
     312        inf->trackerTiers = 0;
     313        inf->trackerList = calloc( sizeof( inf->trackerList[0] ),
     314                                   val->val.l.count );
     315
     316        /* iterate through the announce-list's tiers */
     317        for( ii = 0; ii < val->val.l.count; ii++ )
     318        {
     319            subval = &val->val.l.vals[ii];
     320            if( TYPE_LIST != subval->type || 0 >= subval->val.l.count )
     321            {
     322                continue;
     323            }
     324            subcount = 0;
     325            sublist = calloc( sizeof( sublist[0] ), subval->val.l.count );
     326
     327            /* iterate through the tier's items */
     328            for( jj = 0; jj < subval->val.l.count; jj++ )
     329            {
     330                urlval = &subval->val.l.vals[jj];
     331                if( TYPE_STR != urlval->type ||
     332                    tr_httpParseUrl( urlval->val.s.s, urlval->val.s.i,
     333                                     &address, &port, &announce ) )
     334                {
     335                    continue;
     336                }
     337
     338                /* place the item info in a random location in the sublist */
     339                random = tr_rand( subcount + 1 );
     340                if( random != subcount )
     341                {
     342                    sublist[subcount] = sublist[random];
     343                }
     344                sublist[random].address  = address;
     345                sublist[random].port     = port;
     346                sublist[random].announce = announce;
     347                subcount++;
     348            }
     349
     350            /* just use sublist as is if it's full */
     351            if( subcount == subval->val.l.count )
     352            {
     353                inf->trackerList[inf->trackerTiers].list = sublist;
     354                inf->trackerList[inf->trackerTiers].count = subcount;
     355                inf->trackerTiers++;
     356            }
     357            /* if we skipped some of the tier's items then trim the sublist */
     358            else if( 0 < subcount )
     359            {
     360                inf->trackerList[inf->trackerTiers].list = calloc( sizeof( sublist[0] ), subcount );
     361                memcpy( inf->trackerList[inf->trackerTiers].list, sublist,
     362                        sizeof( sublist[0] ) * subcount );
     363                inf->trackerList[inf->trackerTiers].count = subcount;
     364                inf->trackerTiers++;
     365                free( sublist );
     366            }
     367            /* drop the whole sublist if we didn't use any items at all */
     368            else
     369            {
     370                free( sublist );
     371            }
     372        }
     373
     374        /* did we use any of the tiers? */
     375        if( 0 == inf->trackerTiers )
     376        {
     377            tr_inf( "No valid entries in \"announce-list\"" );
     378            free( inf->trackerList );
     379            inf->trackerList = NULL;
     380        }
     381        /* trim unused sublist pointers */
     382        else if( inf->trackerTiers < val->val.l.count )
     383        {
     384            swapping = inf->trackerList;
     385            inf->trackerList = calloc( sizeof( inf->trackerList[0] ),
     386                                       inf->trackerTiers );
     387            memcpy( inf->trackerList, swapping,
     388                    sizeof( inf->trackerList[0] ) * inf->trackerTiers );
     389            free( swapping );
     390        }
     391    }
     392
     393    /* Regular announce value */
     394    if( 0 == inf->trackerTiers )
     395    {
     396        val = tr_bencDictFind( meta, "announce" );
     397        if( NULL == val || TYPE_STR != val->type )
     398        {
     399            tr_err( "No \"announce\" entry" );
     400            return 1;
     401        }
     402
     403        if( tr_httpParseUrl( val->val.s.s, val->val.s.i,
     404                             &address, &port, &announce ) )
     405        {
     406            tr_err( "Invalid announce URL (%s)", val->val.s.s );
     407            return 1;
     408        }
     409
     410        sublist                   = calloc( sizeof( sublist[0] ), 1 );
     411        sublist[0].address        = address;
     412        sublist[0].port           = port;
     413        sublist[0].announce       = announce;
     414        inf->trackerList          = calloc( sizeof( inf->trackerList[0] ), 1 );
     415        inf->trackerList[0].list  = sublist;
     416        inf->trackerList[0].count = 1;
     417        inf->trackerTiers         = 1;
     418    }
     419
    306420    return 0;
    307421}
  • trunk/libtransmission/metainfo.h

    r310 r1242  
    2828int tr_metainfoParse( tr_info_t *, const char * path,
    2929                      const char * savedHash, int saveCopy );
     30void tr_metainfoFree( tr_info_t * inf );
     31void tr_metainfoRemoveSaved( const char * hashString );
    3032
    31 void tr_metainfoRemoveSaved( const char * hashString );
    3233#endif
  • trunk/libtransmission/tracker.c

    r1149 r1242  
    2525#include "transmission.h"
    2626
     27typedef struct tr_announce_list_ptr_s tr_announce_list_ptr_t;
     28struct tr_announce_list_ptr_s
     29{
     30    tr_tracker_info_t * item;
     31    tr_announce_list_ptr_t * nextItem;
     32};
     33
    2734struct tr_tracker_s
    2835{
     
    3138    char         * id;
    3239    char         * trackerid;
     40   
     41    const char   * trackerAddress;
     42    int            trackerPort;
     43    const char   * trackerAnnounce;
     44    char           trackerScrape[MAX_PATH_LENGTH];
     45    int            trackerCanScrape;
     46   
     47    tr_announce_list_ptr_t ** trackerAnnounceListPtr;
     48
     49#define TC_CHANGE_NO        0
     50#define TC_CHANGE_NEXT      1
     51#define TC_CHANGE_NONEXT    2
     52#define TC_CHANGE_REDIRECT  4
     53    int            shouldChangeAnnounce;
     54    int            announceTier;
     55    int            announceTierLast;
     56   
     57    char         * redirectAddress;
     58    int            redirectAddressLen;
     59    char         * redirectScrapeAddress;
     60    int            redirectScrapeAddressLen;
    3361
    3462    char           started;
     
    4371    int            hasManyPeers;
    4472    int            complete;
     73    int            randOffset;
     74   
     75    int            completelyUnconnectable;
    4576
    4677    uint64_t       dateTry;
     
    6293};
    6394
     95static int         announceToScrape ( char * announce, char * scrape );
     96static void        setAnnounce      ( tr_tracker_t * tc, tr_announce_list_ptr_t * announceItem );
     97static void        failureAnnouncing( tr_tracker_t * tc );
    6498static tr_http_t * getQuery         ( tr_tracker_t * tc );
    6599static tr_http_t * getScrapeQuery   ( tr_tracker_t * tc );
     
    70104tr_tracker_t * tr_trackerInit( tr_torrent_t * tor )
    71105{
     106    tr_info_t * inf = &tor->info;
     107
    72108    tr_tracker_t * tc;
     109    tr_announce_list_ptr_t * prev, * cur;
     110    int ii, jj;
    73111
    74112    tc                 = calloc( 1, sizeof( tr_tracker_t ) );
     
    77115
    78116    tc->started        = 1;
     117   
     118    tc->shouldChangeAnnounce = TC_CHANGE_NO;
     119    tc->redirectAddress = NULL;
    79120
    80121    tc->interval       = 300;
    81122    tc->scrapeInterval = 600;
    82     tc->seeders        = -1;
    83     tc->leechers       = -1;
    84     tc->complete       = -1;
    85123
    86124    tc->lastAttempt    = TC_ATTEMPT_NOREACH;
     
    88126    tc->bindPort       = *(tor->bindPort);
    89127    tc->newPort        = -1;
     128   
     129    tc->trackerAnnounceListPtr = calloc( sizeof( int ), inf->trackerTiers );
     130    for( ii = 0; ii < inf->trackerTiers; ii++ )
     131    {
     132        prev = NULL;
     133        for( jj = 0; jj < inf->trackerList[ii].count; jj++ )
     134        {
     135            cur = calloc( sizeof( tr_announce_list_ptr_t ), 1 );
     136            cur->item = &inf->trackerList[ii].list[jj];
     137            if( NULL == prev )
     138            {
     139                tc->trackerAnnounceListPtr[ii] = cur;
     140            }
     141            else
     142            {
     143                prev->nextItem = cur;
     144            }
     145            prev = cur;
     146        }
     147    }
     148   
     149    setAnnounce( tc, tc->trackerAnnounceListPtr[0] );
    90150
    91151    return tc;
     152}
     153
     154static int announceToScrape( char * announce, char * scrape )
     155{   
     156    char * slash, * nextSlash;
     157    int pre, post;
     158   
     159    slash = strchr( announce, '/' );
     160    while( ( nextSlash = strchr( slash + 1, '/' ) ) )
     161    {
     162        slash = nextSlash;
     163    }
     164    slash++;
     165   
     166    if( !strncmp( slash, "announce", 8 ) )
     167    {
     168        pre  = (long) slash - (long) announce;
     169        post = strlen( announce ) - pre - 8;
     170        memcpy( scrape, announce, pre );
     171        sprintf( &scrape[pre], "scrape" );
     172        memcpy( &scrape[pre+6], &announce[pre+8], post );
     173        scrape[pre+6+post] = 0;
     174       
     175        return 1;
     176    }
     177    else
     178    {
     179        return 0;
     180    }
     181}
     182
     183static void setAnnounce( tr_tracker_t * tc, tr_announce_list_ptr_t * announcePtr )
     184{
     185    tr_tracker_info_t * announceItem = announcePtr->item;
     186   
     187    tc->trackerAddress  = announceItem->address;
     188    tc->trackerPort     = announceItem->port;
     189    tc->trackerAnnounce = announceItem->announce;
     190   
     191    tc->trackerCanScrape = announceToScrape( announceItem->announce, tc->trackerScrape );
     192   
     193    /* Needs a new scrape */
     194    tc->seeders = -1;
     195    tc->leechers = -1;
     196    tc->complete = -1;
     197    tc->dateScrape = 0;
     198}
     199
     200static void failureAnnouncing( tr_tracker_t * tc )
     201{
     202    tr_info_t * inf = &tc->tor->info;
     203   
     204    tc->shouldChangeAnnounce = tc->announceTier + 1 < inf->trackerTiers
     205                                || tc->announceTierLast + 1 < inf->trackerList[tc->announceTier].count
     206                                ? TC_CHANGE_NEXT : TC_CHANGE_NONEXT;
     207   
     208    if( tc->shouldChangeAnnounce == TC_CHANGE_NONEXT )
     209    {
     210        tc->completelyUnconnectable = 1;
     211    }
    92212}
    93213
     
    96216    tr_torrent_t * tor = tc->tor;
    97217    uint64_t       now;
    98 
     218   
     219    /* Last tracker failed, try next */
     220    if( tc->shouldChangeAnnounce == TC_CHANGE_NEXT
     221        || tc->shouldChangeAnnounce == TC_CHANGE_REDIRECT )
     222    {
     223        return 1;
     224    }
     225   
    99226    now = tr_date();
    100227
    101     /* Unreachable tracker, try 10 seconds before trying again */
     228    /* Unreachable tracker, wait 10 seconds + random value before trying again */
    102229    if( tc->lastAttempt == TC_ATTEMPT_NOREACH &&
    103         now < tc->dateTry + 10000 )
     230        now < tc->dateTry + tc->randOffset + 10000 )
    104231    {
    105232        return 0;
     
    110237       anyway */
    111238    if( tc->lastAttempt == TC_ATTEMPT_ERROR &&
    112         now < tc->dateTry + 1000 * tc->interval )
     239        now < tc->dateTry + 1000 * tc->interval + tc->randOffset )
    113240    {
    114241        return 0;
     
    122249
    123250    /* Should we try and get more peers? */
    124     if( now > tc->dateOk + 1000 * tc->interval )
     251    if( now > tc->dateOk + 1000 * tc->interval + tc->randOffset )
    125252    {
    126253        return 1;
     
    164291    uint64_t now, interval;
    165292
    166     /* scrape not supported */
    167     if( !tc->tor->scrape[0] )
     293    /* in process of changing tracker or scrape not supported */
     294    if( tc->shouldChangeAnnounce != TC_CHANGE_NO || !tc->trackerCanScrape )
    168295    {
    169296        return 0;
     
    187314}
    188315
    189 int tr_trackerPulse( tr_tracker_t * tc )
     316void tr_trackerPulse( tr_tracker_t * tc )
    190317{
    191318    tr_torrent_t * tor = tc->tor;
    192319    tr_info_t    * inf = &tor->info;
    193320    const char   * data;
    194     int            len;
     321    char         * address, * announce;
     322    int            len, i, port;
     323    tr_announce_list_ptr_t * announcePtr, * prevAnnouncePtr;
    195324
    196325    if( ( NULL == tc->http ) && shouldConnect( tc ) )
    197326    {
     327        tc->completelyUnconnectable = 0;
     328        tc->randOffset = tr_rand( 60000 );
     329       
    198330        if( tr_fdSocketWillCreate( tor->fdlimit, 1 ) )
    199331        {
    200             return 0;
     332            return;
    201333        }
    202334        tc->dateTry = tr_date();
    203         tc->http = getQuery( tc );
    204 
    205         tr_inf( "Tracker: connecting to %s:%d (%s)",
    206                 inf->trackerAddress, inf->trackerPort,
    207                 tc->started ? "sending 'started'" :
    208                 ( tc->completed ? "sending 'completed'" :
    209                   ( tc->stopped ? "sending 'stopped'" :
    210                     ( 0 < tc->newPort ? "sending 'stopped' to change port" :
    211                       "getting peers" ) ) ) );
     335       
     336        /* Use redirected address */
     337        if( tc->shouldChangeAnnounce == TC_CHANGE_REDIRECT )
     338        {
     339            if( !tr_httpParseUrl( tc->redirectAddress, tc->redirectAddressLen,
     340                                     &address, &port, &announce ) )
     341            {
     342                tr_err( "Tracker: redirected URL: %s:%d", address, port );
     343                tc->http = tr_httpClient( TR_HTTP_GET, address, port, announce );
     344               
     345                free( address );
     346                free( announce );
     347            }
     348           
     349            free( tc->redirectAddress );
     350            tc->redirectAddress = NULL;
     351        }
     352        else
     353        {
     354            /* Need to change to next address in list */
     355            if( tc->shouldChangeAnnounce == TC_CHANGE_NEXT )
     356            {
     357                tr_inf( "Tracker: failed to connect to %s, trying next", tc->trackerAddress );
     358               
     359                if( tc->announceTierLast + 1 < inf->trackerList[tc->announceTier].count )
     360                {
     361                    tc->announceTierLast++;
     362                   
     363                    announcePtr = tc->trackerAnnounceListPtr[tc->announceTier];
     364                    for( i = 0; i < tc->announceTierLast; i++ )
     365                    {
     366                        announcePtr = announcePtr->nextItem;
     367                    }
     368                }
     369                else
     370                {
     371                    tc->announceTierLast = 0;
     372                    tc->announceTier++;
     373                   
     374                    announcePtr = tc->trackerAnnounceListPtr[tc->announceTier];
     375                }
     376               
     377                tr_inf( "Tracker: tracker address set to %s", tc->trackerAnnounceListPtr[tc->announceTier]->item->address );
     378                setAnnounce( tc, announcePtr );
     379            }
     380            /* Need to change to first in list */
     381            else if( tc->announceTier != 0 || tc->announceTierLast != 0 )
     382            {
     383                /* Check if the last announce was successful and wasn't the first in the sublist */
     384                if( tc->shouldChangeAnnounce == TC_CHANGE_NO && tc->announceTierLast != 0 )
     385                {
     386                    announcePtr = tc->trackerAnnounceListPtr[tc->announceTier];
     387                    for( i = 0; i < tc->announceTierLast; i++ )
     388                    {
     389                        prevAnnouncePtr = announcePtr;
     390                        announcePtr = announcePtr->nextItem;
     391                    }
     392                   
     393                    /* Move address to front of tier in announce list */
     394                    prevAnnouncePtr->nextItem = announcePtr->nextItem;
     395                    announcePtr->nextItem =  tc->trackerAnnounceListPtr[tc->announceTier];
     396                    tc->trackerAnnounceListPtr[tc->announceTier] = announcePtr;
     397                }
     398               
     399                setAnnounce( tc, tc->trackerAnnounceListPtr[0] );
     400                tc->announceTier = 0;
     401                tc->announceTierLast = 0;
     402            }
     403           
     404            tc->http = getQuery( tc );
     405
     406            tr_inf( "Tracker: connecting to %s:%d (%s)",
     407                    tc->trackerAddress, tc->trackerPort,
     408                    tc->started ? "sending 'started'" :
     409                    ( tc->completed ? "sending 'completed'" :
     410                      ( tc->stopped ? "sending 'stopped'" :
     411                        ( 0 < tc->newPort ? "sending 'stopped' to change port" :
     412                          "getting peers" ) ) ) );
     413        }
     414       
     415        tc->shouldChangeAnnounce = TC_CHANGE_NO;
    212416    }
    213417
     
    222426                killHttp( &tc->http, tor->fdlimit );
    223427                tc->dateTry = tr_date();
     428               
     429                failureAnnouncing( tc );
     430                if ( tc->shouldChangeAnnounce == TC_CHANGE_NEXT )
     431                {
     432                    tr_trackerPulse( tc );
     433                    return;
     434                }
     435               
    224436                break;
    225437
     
    227439                readAnswer( tc, data, len );
    228440                killHttp( &tc->http, tor->fdlimit );
     441               
     442                /* Something happened to need to try next address */
     443                if ( tc->shouldChangeAnnounce == TC_CHANGE_NEXT
     444                    || tc->shouldChangeAnnounce == TC_CHANGE_REDIRECT )
     445                {
     446                    tr_trackerPulse( tc );
     447                    return;
     448                }
     449               
    229450                break;
    230451        }
     
    235456        if( tr_fdSocketWillCreate( tor->fdlimit, 1 ) )
    236457        {
    237             return 0;
     458            return;
    238459        }
    239460        tc->dateScrape = tr_date();
    240         tc->httpScrape = getScrapeQuery( tc );
    241         tr_inf( "Scrape: sent http request to %s:%d",
    242                     inf->trackerAddress, inf->trackerPort );
     461       
     462        if ( tc->redirectScrapeAddress != NULL )
     463        {
     464            /* Use redirected address */
     465            if( !tr_httpParseUrl( tc->redirectScrapeAddress, tc->redirectScrapeAddressLen,
     466                                     &address, &port, &announce ) )
     467            {
     468                tr_err( "Scrape: redirected URL: %s:%d", address, port );
     469                tc->httpScrape = tr_httpClient( TR_HTTP_GET, address, port, announce );
     470               
     471                free( address );
     472                free( announce );
     473            }
     474           
     475            free( tc->redirectScrapeAddress );
     476            tc->redirectScrapeAddress = NULL;
     477        }
     478        else
     479        {
     480            tc->httpScrape = getScrapeQuery( tc );
     481            tr_inf( "Scrape: sent HTTP request to %s:%d%s", tc->trackerAddress, tc->trackerPort, tc->trackerScrape );
     482        }
    243483    }
    244484
     
    262502    }
    263503
    264     return 0;
     504    return;
    265505}
    266506
     
    301541{
    302542    tr_torrent_t * tor = tc->tor;
    303     tr_info_t    * inf = &tor->info;
    304543
    305544    char         * event, * trackerid, * idparam;
     
    349588    }
    350589
    351     start = ( strchr( inf->trackerAnnounce, '?' ) ? '&' : '?' );
     590    start = ( strchr( tc->trackerAnnounce, '?' ) ? '&' : '?' );
    352591    left  = tr_cpLeftBytes( tor->completion );
    353592
    354     return tr_httpClient( TR_HTTP_GET, inf->trackerAddress, inf->trackerPort,
     593    return tr_httpClient( TR_HTTP_GET, tc->trackerAddress, tc->trackerPort,
    355594                          "%s%c"
    356595                          "info_hash=%s&"
     
    365604                          "%s%s"
    366605                          "%s",
    367                           inf->trackerAnnounce, start, tor->escapedHashString,
     606                          tc->trackerAnnounce, start, tor->escapedHashString,
    368607                          tc->id, tc->bindPort, up, down, left, numwant,
    369608                          tor->key, idparam, trackerid, event );
     
    373612{
    374613    tr_torrent_t * tor = tc->tor;
    375     tr_info_t    * inf = &tor->info;
    376 
    377614    char           start;
    378615
    379     start = ( strchr( tor->scrape, '?' ) ? '&' : '?' );
    380 
    381     return tr_httpClient( TR_HTTP_GET, inf->trackerAddress, inf->trackerPort,
     616    start = ( strchr( tc->trackerScrape, '?' ) ? '&' : '?' );
     617
     618    return tr_httpClient( TR_HTTP_GET, tc->trackerAddress, tc->trackerPort,
    382619                          "%s%c"
    383620                          "info_hash=%s",
    384                           tor->scrape, start, tor->escapedHashString );
     621                          tc->trackerScrape, start, tor->escapedHashString );
    385622}
    386623
     
    397634    tc->dateTry = tr_date();
    398635    code = tr_httpResponseCode( data, len );
     636   
    399637    if( 0 > code )
    400638    {
     
    402640        tr_inf( "Tracker: invalid HTTP status line" );
    403641        tc->lastAttempt = TC_ATTEMPT_NOREACH;
     642        failureAnnouncing( tc );
     643        return;
     644    }
     645   
     646    if( code == 301 || code == 302 )
     647    {
     648        tr_err( "Tracker: HTTP status code: %i", code );
     649       
     650        tr_http_header_t hdr[] = { { "Location", NULL, 0 }, { NULL, NULL, 0 } };
     651       
     652        tr_httpParse( data, len, hdr );
     653       
     654        char * address = calloc( sizeof( char ), hdr->len+1 );
     655        snprintf( address, hdr->len+1, "%s", hdr->data );
     656       
     657        tc->shouldChangeAnnounce = TC_CHANGE_REDIRECT;
     658        tc->redirectAddress = address;
     659        tc->redirectAddressLen = hdr->len;
     660       
    404661        return;
    405662    }
     
    410667        tr_err( "Tracker: invalid HTTP status code: %i", code );
    411668        tc->lastAttempt = TC_ATTEMPT_ERROR;
     669        failureAnnouncing( tc );
    412670        return;
    413671    }
     
    419677        tr_err( "Tracker: could not find end of HTTP headers" );
    420678        tc->lastAttempt = TC_ATTEMPT_NOREACH;
     679        failureAnnouncing( tc );
    421680        return;
    422681    }
     
    443702        tr_err( "Tracker: no valid dictionary found in answer" );
    444703        tc->lastAttempt = TC_ATTEMPT_ERROR;
     704        failureAnnouncing( tc );
    445705        return;
    446706    }
     
    450710    if( ( bePeers = tr_bencDictFind( &beAll, "failure reason" ) ) )
    451711    {
    452         tr_err( "Tracker: %s", bePeers->val.s.s );
     712        tr_err( "Tracker: Error - %s", bePeers->val.s.s );
    453713        tor->error |= TR_ETRACKER;
    454714        snprintf( tor->trackerError, sizeof( tor->trackerError ),
    455715                  "%s", bePeers->val.s.s );
    456716        tc->lastAttempt = TC_ATTEMPT_ERROR;
     717        failureAnnouncing( tc );
    457718        goto cleanup;
     719    }
     720    else if( ( bePeers = tr_bencDictFind( &beAll, "warning message" ) ) )
     721    {
     722        tr_err( "Tracker: Warning - %s", bePeers->val.s.s );
     723        snprintf( tor->trackerError, sizeof( tor->trackerError ),
     724                  "%s", bePeers->val.s.s );
     725    }
     726    else
     727    {
     728        tor->trackerError[0] = '\0';
    458729    }
    459730
     
    543814        }
    544815        tr_err( "Tracker: no \"peers\" field" );
     816        failureAnnouncing( tc );
    545817        goto cleanup;
    546818    }
     
    640912        return;
    641913    }
     914   
     915    if( code == 301 || code == 302 )
     916    {
     917        tr_err( "Scrape: HTTP status code: %i", code );
     918       
     919        tr_http_header_t hdr[] = { { "Location", NULL, 0 }, { NULL, NULL, 0 } };
     920       
     921        tr_httpParse( data, len, hdr );
     922       
     923        char * address = calloc( sizeof( char ), hdr->len+1 );
     924        snprintf( address, hdr->len+1, "%s", hdr->data );
     925       
     926        /* Needs a new scrape */
     927        tc->dateScrape = 0;
     928       
     929        tc->redirectScrapeAddress = address;
     930        tc->redirectScrapeAddressLen = hdr->len;
     931       
     932        return;
     933    }
    642934
    643935    if( !TR_HTTP_STATUS_OK( code ) )
     
    645937        /* we didn't get a 2xx status code */
    646938        tr_err( "Scrape: invalid HTTP status code: %i", code );
     939        if( TR_HTTP_STATUS_FAIL_CLIENT( code ) )
     940            tc->trackerCanScrape = 0;
    647941        tc->lastScrapeFailed = 1;
    648942        return;
     
    7501044    }
    7511045    return tc->complete;
     1046}
     1047
     1048const char * tr_trackerAddress( tr_tracker_t * tc )
     1049{
     1050    if( !tc )
     1051    {
     1052        return NULL;
     1053    }
     1054    return tc->trackerAddress;
     1055}
     1056
     1057int tr_trackerPort( tr_tracker_t * tc )
     1058{
     1059    if( !tc )
     1060    {
     1061        return 0;
     1062    }
     1063    return tc->trackerPort;
     1064}
     1065
     1066const char * tr_trackerAnnounce( tr_tracker_t * tc )
     1067{
     1068    if( !tc )
     1069    {
     1070        return NULL;
     1071    }
     1072    return tc->trackerAnnounce;
     1073}
     1074
     1075int tr_trackerCannotConnecting( tr_tracker_t * tc )
     1076{
     1077    if( !tc )
     1078    {
     1079        return 0;
     1080    }
     1081    return tc->completelyUnconnectable;
    7521082}
    7531083
     
    7601090    int            len;
    7611091    int            ret;
    762 
    763     if( !tor->scrape[0] )
     1092   
     1093    tc = tr_trackerInit( tor );
     1094
     1095    if( !tc->trackerCanScrape )
    7641096    {
    7651097        return 1;
    7661098    }
    7671099
    768     tc = tr_trackerInit( tor );
    7691100    http = getScrapeQuery( tc );
    7701101
  • trunk/libtransmission/tracker.h

    r1149 r1242  
    3030tr_tracker_t * tr_trackerInit      ( tr_torrent_t * );
    3131void           tr_trackerChangePort( tr_tracker_t *, int );
    32 int            tr_trackerPulse     ( tr_tracker_t * );
     32void           tr_trackerPulse     ( tr_tracker_t * );
    3333void           tr_trackerCompleted ( tr_tracker_t * );
    3434void           tr_trackerStopped   ( tr_tracker_t * );
     
    5757int tr_trackerDownloaded( tr_tracker_t * tc );
    5858
     59const char * tr_trackerAddress ( tr_tracker_t * tc );
     60int          tr_trackerPort    ( tr_tracker_t * tc );
     61const char * tr_trackerAnnounce( tr_tracker_t * tc );
     62
     63int tr_trackerCannotConnecting( tr_tracker_t * tc );
     64
    5965/***********************************************************************
    6066 * tr_trackerScrape
  • trunk/libtransmission/transmission.c

    r1149 r1242  
    3131                                       int flags, int * error );
    3232static void torrentReallyStop( tr_torrent_t * );
    33 static void  downloadLoop( void * );
    34 static void  acceptLoop( void * );
     33static void downloadLoop( void * );
     34static void acceptLoop( void * );
    3535static void acceptStop( tr_handle_t * h );
    3636
     
    278278    tr_info_t     * inf;
    279279    int             i;
    280     char          * s1, * s2;
    281280
    282281    inf        = &tor->info;
     
    290289        {
    291290            *error = TR_EDUPLICATE;
    292             free( inf->pieces );
    293             free( inf->files );
     291            tr_metainfoFree( &tor->info );
    294292            free( tor );
    295293            return NULL;
     
    302300    tor->bindPort = &h->bindPort;
    303301        tor->finished = 0;
    304 
    305 
    306     /* Guess scrape URL */
    307     s1 = strchr( inf->trackerAnnounce, '/' );
    308     while( ( s2 = strchr( s1 + 1, '/' ) ) )
    309     {
    310         s1 = s2;
    311     }
    312     s1++;
    313     if( !strncmp( s1, "announce", 8 ) )
    314     {
    315         int pre  = (long) s1 - (long) inf->trackerAnnounce;
    316         int post = strlen( inf->trackerAnnounce ) - pre - 8;
    317         memcpy( tor->scrape, inf->trackerAnnounce, pre );
    318         sprintf( &tor->scrape[pre], "scrape" );
    319         memcpy( &tor->scrape[pre+6], &inf->trackerAnnounce[pre+8], post );
    320     }
    321302
    322303    /* Escaped info hash for HTTP queries */
     
    474455    tr_peer_t * peer;
    475456    tr_info_t * inf = &tor->info;
     457    tr_tracker_t * tc = tor->tracker;
    476458    int i;
    477459
     
    493475    memcpy( s->trackerError, tor->trackerError,
    494476            sizeof( s->trackerError ) );
     477    s->cannotConnect = tr_trackerCannotConnecting( tc );
     478   
     479    if( tor->tracker )
     480    {
     481        s->trackerAddress  = tr_trackerAddress(  tor->tracker );
     482        s->trackerPort     = tr_trackerPort(     tor->tracker );
     483        s->trackerAnnounce = tr_trackerAnnounce( tor->tracker );
     484    }
     485    else
     486    {
     487        s->trackerAddress  = inf->trackerList[0].list[0].address;
     488        s->trackerPort     = inf->trackerList[0].list[0].port;
     489        s->trackerAnnounce = inf->trackerList[0].list[0].announce;
     490    }
    495491
    496492    s->peersTotal       = 0;
     
    538534    s->rateUpload = tr_rcRate( tor->upload );
    539535   
    540     s->seeders  = tr_trackerSeeders(tor->tracker);
    541     s->leechers = tr_trackerLeechers(tor->tracker);
     536    s->seeders  = tr_trackerSeeders( tc );
     537    s->leechers = tr_trackerLeechers( tc );
     538    s->completedFromTracker = tr_trackerDownloaded( tor->tracker );
    542539    s->completedFromTracker = tr_trackerDownloaded(tor->tracker);
    543540
     
    690687        free( tor->destination );
    691688    }
    692     free( inf->pieces );
    693     free( inf->files );
     689
     690    tr_metainfoFree( inf );
    694691
    695692    if( tor->prev )
  • trunk/libtransmission/transmission.h

    r1149 r1242  
    6262tr_handle_t * tr_init();
    6363
     64typedef struct tr_tracker_info_s tr_tracker_info_t;
     65
    6466/***********************************************************************
    6567 * tr_setMessageLevel
     
    304306{
    305307    /* Path to torrent */
    306     char        torrent[MAX_PATH_LENGTH];
     308    char                 torrent[MAX_PATH_LENGTH];
    307309
    308310    /* General info */
    309     uint8_t     hash[SHA_DIGEST_LENGTH];
    310     char        hashString[2*SHA_DIGEST_LENGTH+1];
    311     char        name[MAX_PATH_LENGTH];
     311    uint8_t              hash[SHA_DIGEST_LENGTH];
     312    char                 hashString[2*SHA_DIGEST_LENGTH+1];
     313    char                 name[MAX_PATH_LENGTH];
    312314
    313315    /* Flags */
    314316#define TR_FSAVEPRIVATE 0x01    /* save a private copy of the torrent */
    315     int         flags;
     317    int                  flags;
    316318
    317319    /* Tracker info */
    318     char        trackerAddress[256];
    319     int         trackerPort;
    320     char        trackerAnnounce[MAX_PATH_LENGTH];
    321    
     320    struct
     321    {
     322        tr_tracker_info_t * list;
     323        int                 count;
     324    }                  * trackerList;
     325    int                  trackerTiers;
     326
    322327    /* Torrent info */
    323     char        comment[MAX_PATH_LENGTH];
    324     char        creator[MAX_PATH_LENGTH];
    325     int         dateCreated;
     328    char                 comment[MAX_PATH_LENGTH];
     329    char                 creator[MAX_PATH_LENGTH];
     330    int                  dateCreated;
     331    int                  privateTorrent;
    326332
    327333    /* Pieces info */
    328     int         pieceSize;
    329     int         pieceCount;
    330     uint64_t    totalSize;
    331     uint8_t   * pieces;
     334    int                  pieceSize;
     335    int                  pieceCount;
     336    uint64_t             totalSize;
     337    uint8_t            * pieces;
    332338
    333339    /* Files info */
    334     int         multifile;
    335     int         fileCount;
    336     tr_file_t * files;
     340    int                  multifile;
     341    int                  fileCount;
     342    tr_file_t          * files;
    337343};
    338344
     
    358364    int                 error;
    359365    char                trackerError[128];
     366    int                 cannotConnect;
     367   
     368    const char        * trackerAddress;
     369    int                 trackerPort;
     370    const char        * trackerAnnounce;
    360371
    361372    float               progress;
     
    396407};
    397408
     409struct tr_tracker_info_s
     410{
     411    char * address;
     412    int    port;
     413    char * announce;
     414};
     415
    398416#ifdef __TRANSMISSION__
    399417#  include "internal.h"
  • trunk/macosx/English.lproj/InfoWindow.nib/classes.nib

    r1149 r1242  
    2929                fDownloadedValidField = NSTextField;
    3030                fDownloadingFromField = NSTextField;
     31                fErrorMessageView = NSTextView;
    3132                fFileTable = NSTableView;
    3233                fFileTableStatusField = NSTextField;
     
    4344                fRevealDataButton = NSButton;
    4445                fRevealTorrentButton = NSButton;
     46                fSecureField = NSTextField;
    4547                fSeedersField = NSTextField;
    4648                fSizeField = NSTextField;
  • trunk/macosx/InfoWindowController.h

    r1149 r1242  
    3838    IBOutlet NSImageView * fImageView;
    3939    IBOutlet NSTextField * fNameField, * fSizeField, * fTrackerField,
    40                         * fPiecesField, * fHashField,
     40                        * fPiecesField, * fHashField, * fSecureField,
    4141                        * fTorrentLocationField, * fDataLocationField,
    4242                        * fDateStartedField,
     
    5151    IBOutlet NSTextField * fSeedersField, * fLeechersField, * fConnectedPeersField,
    5252                        * fDownloadingFromField, * fUploadingToField, * fCompletedFromTrackerField;
     53    IBOutlet NSTextView * fErrorMessageView;
     54    IBOutlet PiecesView * fPiecesView;
    5355   
    5456    IBOutlet NSTableView * fFileTable;
     
    5759    IBOutlet NSMatrix * fRatioMatrix;
    5860    IBOutlet NSTextField * fRatioLimitField;
    59    
    60     IBOutlet PiecesView * fPiecesView;
    6161}
    6262
  • trunk/macosx/InfoWindowController.m

    r1149 r1242  
    4040
    4141//15 spacing at the bottom of each tab
    42 #define TAB_INFO_HEIGHT 268.0
    43 #define TAB_ACTIVITY_HEIGHT 109.0
     42#define TAB_INFO_HEIGHT 284.0
     43#define TAB_ACTIVITY_HEIGHT 170.0
    4444#define TAB_PEERS_HEIGHT 268.0
    4545#define TAB_FILES_HEIGHT 268.0
     
    5050@interface InfoWindowController (Private)
    5151
     52- (void) updateInfoGeneral;
    5253- (void) updateInfoActivity;
    5354- (void) updateInfoPeers;
     
    155156        [fHashField setStringValue: @""];
    156157        [fHashField setToolTip: nil];
     158        [fSecureField setStringValue: @""];
    157159        [fCommentView setString: @""];
    158         [fCommentView setSelectable: NO];
    159160       
    160161        [fCreatorField setStringValue: @""];
     
    166167        [fDataLocationField setToolTip: nil];
    167168        [fDateStartedField setStringValue: @""];
     169        [fCommentView setSelectable: NO];
    168170       
    169171        [fRevealDataButton setHidden: YES];
     
    187189        [fUploadingToField setStringValue: @""];
    188190        [fSwarmSpeedField setStringValue: @""];
     191        [fErrorMessageView setString: @""];
     192        [fErrorMessageView setSelectable: NO];
    189193       
    190194        [fPeers removeAllObjects];
     
    204208        [fSizeField setStringValue: [NSString stringForFileSize: [torrent size]]];
    205209       
    206         NSString * tracker = [[torrent tracker] stringByAppendingString: [torrent announce]],
    207                 * hashString = [torrent hashString],
     210        NSString * hashString = [torrent hashString],
    208211                * commentString = [torrent comment];
    209         [fTrackerField setStringValue: tracker];
    210         [fTrackerField setToolTip: tracker];
    211212        [fPiecesField setStringValue: [NSString stringWithFormat: @"%d, %@", [torrent pieceCount],
    212213                                        [NSString stringForFileSize: [torrent pieceSize]]]];
    213214        [fHashField setStringValue: hashString];
    214215        [fHashField setToolTip: hashString];
     216        [fSecureField setStringValue: [torrent privateTorrent]
     217                        ? NSLocalizedString(@"Private Torrent", "Inspector -> is private torrent")
     218                        : NSLocalizedString(@"Public Torrent", "Inspector -> is not private torrent")];
    215219        [fCommentView setString: commentString];
    216         [fCommentView setSelectable: YES];
    217220       
    218221        [fCreatorField setStringValue: [torrent creator]];
     
    280283    else if ([[[fTabView selectedTabViewItem] identifier] isEqualToString: TAB_PEERS_IDENT])
    281284        [self updateInfoPeers];
     285    else if ([[[fTabView selectedTabViewItem] identifier] isEqualToString: TAB_INFO_IDENT])
     286        [self updateInfoGeneral];
    282287    else;
     288}
     289
     290- (void) updateInfoGeneral
     291{   
     292    int numberSelected = [fTorrents count];
     293    if (numberSelected != 1)
     294        return;
     295   
     296    Torrent * torrent = [fTorrents objectAtIndex: 0];
     297    NSString * tracker = [[torrent tracker] stringByAppendingString: [torrent announce]];
     298    [fTrackerField setStringValue: tracker];
     299    [fTrackerField setToolTip: tracker];
    283300}
    284301
     
    313330       
    314331        [fStateField setStringValue: [torrent stateString]];
    315        
    316332        [fRatioField setStringValue: [NSString stringForRatioWithDownload: downloadedTotal upload: uploadedTotal]];
    317        
    318333        [fSwarmSpeedField setStringValue: [torrent isActive] ? [NSString stringForSpeed: [torrent swarmSpeed]] : @""];
     334       
     335        NSString * errorMessage = [torrent errorMessage];
     336        if (![errorMessage isEqualToString: [fErrorMessageView string]])
     337        {
     338            [fErrorMessageView setString: errorMessage];
     339            [fErrorMessageView setSelectable: ![errorMessage isEqualToString: @""]];
     340        }
    319341       
    320342        [fPiecesView updateView: NO];
  • trunk/macosx/Torrent.h

    r1149 r1242  
    116116- (int)        pieceCount;
    117117- (NSString *) hashString;
     118- (BOOL)       privateTorrent;
    118119
    119120- (NSString *) torrentLocation;
     
    128129- (int)     eta;
    129130
    130 - (BOOL)    isActive;
    131 - (BOOL)    isSeeding;
    132 - (BOOL)    isPaused;
    133 - (BOOL)    isError;
    134 - (BOOL)    justFinished;
     131- (BOOL)        isActive;
     132- (BOOL)        isSeeding;
     133- (BOOL)        isPaused;
     134- (BOOL)        isError;
     135- (NSString *)  errorMessage;
     136- (BOOL)        justFinished;
    135137
    136138- (NSArray *) peers;
  • trunk/macosx/Torrent.m

    r1149 r1242  
    350350    {
    351351        [fStatusString setString: [NSLocalizedString(@"Error: ", "Torrent -> status string") stringByAppendingString:
    352                                     [NSString stringWithUTF8String: fStat->trackerError]]];
    353         if (!fError && [self isActive])
    354         {
    355             fError = YES;
    356             if (![self isSeeding])
    357                 [[NSNotificationCenter defaultCenter] postNotificationName: @"StoppedDownloading" object: self];
    358         }
    359     }
    360     else
    361     {
    362         if (fError)
    363             fError = NO;
     352                                    [self errorMessage]]];
     353    }
     354   
     355    BOOL wasError = fError;
     356    if ((fError = fStat->cannotConnect))
     357    {
     358        if (!wasError && [self isActive] && ![self isSeeding])
     359            [[NSNotificationCenter defaultCenter] postNotificationName: @"StoppedDownloading" object: self];
    364360    }
    365361
     
    705701- (NSString *) tracker
    706702{
    707     return [NSString stringWithFormat: @"%s:%d", fInfo->trackerAddress, fInfo->trackerPort];
     703    return [NSString stringWithFormat: @"http://%s:%d", fStat->trackerAddress, fStat->trackerPort];
    708704}
    709705
    710706- (NSString *) announce
    711707{
    712     return [NSString stringWithUTF8String: fInfo->trackerAnnounce];
     708    return [NSString stringWithUTF8String: fStat->trackerAnnounce];
    713709}
    714710
     
    742738{
    743739    return [NSString stringWithUTF8String: fInfo->hashString];
     740}
     741
     742- (BOOL) privateTorrent
     743{
     744    return fInfo->privateTorrent;
    744745}
    745746
     
    821822{
    822823    return fStat->error & TR_ETRACKER;
     824}
     825
     826- (NSString *) errorMessage
     827{
     828    [NSString stringWithUTF8String: fStat->trackerError];
    823829}
    824830
Note: See TracChangeset for help on using the changeset viewer.