source: branches/multitracker/libtransmission/tracker.c @ 1155

Last change on this file since 1155 was 1155, checked in by livings124, 15 years ago

some extra free's

  • Property svn:keywords set to Date Rev Author Id
File size: 24.3 KB
Line 
1/******************************************************************************
2 * $Id: tracker.c 1155 2006-12-05 19:03:10Z livings124 $
3 *
4 * Copyright (c) 2005-2006 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include "transmission.h"
26
27struct tr_tracker_s
28{
29    tr_torrent_t * tor;
30
31    char         * id;
32    char         * trackerid;
33
34    char           started;
35    char           completed;
36    char           stopped;
37
38    int            interval;
39    int            minInterval;
40    int            scrapeInterval;
41    int            seeders;
42    int            leechers;
43    int            hasManyPeers;
44    int            complete;
45
46    uint64_t       dateTry;
47    uint64_t       dateOk;
48    uint64_t       dateScrape;
49    int            lastScrapeFailed;
50   
51    int            shouldChangeAnnounce;
52    int            announceTier;
53    int            announceTierLast;
54
55#define TC_ATTEMPT_NOREACH 1
56#define TC_ATTEMPT_ERROR   2
57#define TC_ATTEMPT_OK      4
58    char           lastAttempt;
59    int            scrapeNeeded;
60
61    tr_http_t    * http;
62    tr_http_t    * httpScrape;
63
64    int            bindPort;
65    int            newPort;
66};
67
68static void        setAnnounce( tr_tracker_t * tc, tr_announce_list_item_t * announceItem );
69static int         announceToScrape( char * announce, char * scrape );
70static void        failureAnnouncing( tr_tracker_t * tc );
71static tr_http_t * getQuery         ( tr_tracker_t * tc );
72static tr_http_t * getScrapeQuery   ( tr_tracker_t * tc );
73static void        readAnswer       ( tr_tracker_t * tc, const char *, int );
74static void        readScrapeAnswer ( tr_tracker_t * tc, const char *, int );
75static void        killHttp         ( tr_http_t ** http, tr_fd_t * fdlimit );
76
77tr_tracker_t * tr_trackerInit( tr_torrent_t * tor )
78{
79    tr_tracker_t * tc;
80
81    tc                 = calloc( 1, sizeof( tr_tracker_t ) );
82    tc->tor            = tor;
83    tc->id             = tor->id;
84   
85    setAnnounce( tc, &tor->info.trackerAnnounceList[0] );
86
87    tc->started        = 1;
88
89    tc->interval       = 300;
90    tc->scrapeInterval = 600;
91    tc->seeders        = -1;
92    tc->leechers       = -1;
93    tc->complete       = -1;
94
95    tc->lastAttempt    = TC_ATTEMPT_NOREACH;
96
97    tc->bindPort       = *(tor->bindPort);
98    tc->newPort        = -1;
99
100    return tc;
101}
102
103static void setAnnounce( tr_tracker_t * tc, tr_announce_list_item_t * announceItem )
104{
105    tr_torrent_t * tor = tc->tor;
106    tr_info_t    * inf = &tor->info;
107   
108    tr_lockLock( &tor->lock );
109   
110    snprintf( inf->trackerAddress, 256, "%s", announceItem->address );
111    inf->trackerPort = announceItem->port;
112    snprintf( inf->trackerAnnounce, MAX_PATH_LENGTH, "%s", announceItem->announce );
113   
114    inf->trackerCanScrape = announceToScrape( announceItem->announce, inf->trackerScrape );
115    tc->dateScrape = 0;
116   
117    tr_lockUnlock( &tor->lock );
118}
119
120static int announceToScrape( char * announce, char * scrape )
121{   
122    char * slash, * nextSlash;
123    int pre, post;
124   
125    slash = strchr( announce, '/' );
126    while( ( nextSlash = strchr( slash + 1, '/' ) ) )
127    {
128        slash = nextSlash;
129    }
130    slash++;
131   
132    if( !strncmp( slash, "announce", 8 ) )
133    {
134        pre  = (long) slash - (long) announce;
135        post = strlen( announce ) - pre - 8;
136        memcpy( scrape, announce, pre );
137        sprintf( &scrape[pre], "scrape" );
138        memcpy( &scrape[pre+6], &announce[pre+8], post );
139        scrape[pre+6+post] = 0;
140       
141        return 1;
142    }
143    else
144    {
145        return 0;
146    }
147}
148
149static void failureAnnouncing( tr_tracker_t * tc )
150{
151    tr_torrent_t * tor = tc->tor;
152    tr_info_t    * inf = &tor->info;
153   
154    int i;
155    tr_announce_list_item_t * announceItem;
156   
157    tc->announceTierLast++;
158    tc->shouldChangeAnnounce = 1;
159   
160    /* If there are no more trackers don't try to change the announce */
161    if( tc->announceTier <= inf->trackerAnnounceTiers)
162        return;
163   
164    announceItem = &inf->trackerAnnounceList[tc->announceTier];
165    for( i = 0; i <= tc->announceTierLast; i++ )
166    {
167        announceItem = announceItem->nextItem;
168    }
169   
170    if( announceItem == NULL )
171    {
172        tc->shouldChangeAnnounce = 0;
173    }
174}
175
176static int shouldConnect( tr_tracker_t * tc )
177{
178    tr_torrent_t * tor = tc->tor;
179    uint64_t       now;
180   
181    /* Last tracker failed, try next */
182    if( tc->shouldChangeAnnounce )
183    {
184        return 1;
185    }
186   
187    now = tr_date();
188
189    /* Unreachable tracker, try 10 seconds before trying again */
190    if( tc->lastAttempt == TC_ATTEMPT_NOREACH &&
191        now < tc->dateTry + 10000 )
192    {
193        return 0;
194    }
195
196    /* The tracker rejected us (like 4XX code, unauthorized IP...),
197       don't hammer it - we'll probably get the same answer next time
198       anyway */
199    if( tc->lastAttempt == TC_ATTEMPT_ERROR &&
200        now < tc->dateTry + 1000 * tc->interval )
201    {
202        return 0;
203    }
204
205    /* Do we need to send an event? */
206    if( tc->started || tc->completed || tc->stopped || 0 < tc->newPort )
207    {
208        return 1;
209    }
210
211    /* Should we try and get more peers? */
212    if( now > tc->dateOk + 1000 * tc->interval )
213    {
214        return 1;
215    }
216
217    /* If there is quite a lot of people on this torrent, stress
218       the tracker a bit until we get a decent number of peers */
219    if( tc->hasManyPeers && !tr_cpIsSeeding( tor->completion ) )
220    {
221        /* reannounce in 10 seconds if we have less than 5 peers */
222        if( tor->peerCount < 5 )
223        {
224            if( now > tc->dateOk + 1000 * MAX( 10, tc->minInterval ) )
225            {
226                return 1;
227            }
228        }
229        /* reannounce in 20 seconds if we have less than 10 peers */
230        else if( tor->peerCount < 10 )
231        {
232            if( now > tc->dateOk + 1000 * MAX( 20, tc->minInterval ) )
233            {
234                return 1;
235            }
236        }
237        /* reannounce in 30 seconds if we have less than 15 peers */
238        else if( tor->peerCount < 15 )
239        {
240            if( now > tc->dateOk + 1000 * MAX( 30, tc->minInterval ) )
241            {
242                return 1;
243            }
244        }
245    }
246
247    return 0;
248}
249
250static int shouldScrape( tr_tracker_t * tc )
251{
252    tr_torrent_t * tor = tc->tor;
253    tr_info_t    * inf = &tor->info;
254    uint64_t now, interval;
255
256    /* in process of changing tracker or scrape not supported */
257    if( tc->shouldChangeAnnounce || !inf->trackerCanScrape )
258    {
259        return 0;
260    }
261
262    now      = tr_date();
263    interval = 1000 * MAX( tc->scrapeInterval, 60 );
264
265    /* scrape half as often if there is no need to */
266    if( !tc->scrapeNeeded && !tc->lastScrapeFailed )
267    {
268        interval *= 2;
269    }
270
271    return now > tc->dateScrape + interval;
272}
273
274void tr_trackerChangePort( tr_tracker_t * tc, int port )
275{
276    tc->newPort = port;
277}
278
279int tr_trackerPulse( tr_tracker_t * tc )
280{
281    tr_torrent_t * tor = tc->tor;
282    tr_info_t    * inf = &tor->info;
283    const char   * data;
284    int            len, i;
285    tr_announce_list_item_t * announceItem;
286
287    if( ( NULL == tc->http ) && shouldConnect( tc ) )
288    {
289        if( tc->shouldChangeAnnounce )
290        {
291            tr_err( "Tracker: %s failed to connect, trying next", inf->trackerAddress );
292           
293            announceItem = &inf->trackerAnnounceList[tc->announceTier];
294            for( i = 0; i <= tc->announceTierLast; i++ )
295            {
296                announceItem = announceItem->nextItem;
297            }
298           
299            if( announceItem != NULL )
300            {
301                tc->announceTierLast++;
302               
303                /* Move address to front of tier in announce list */
304            }
305            else
306            {
307                tc->announceTierLast = 0;
308                tc->announceTier++;
309               
310                announceItem = &inf->trackerAnnounceList[tc->announceTier];
311            }
312           
313            setAnnounce( tc, announceItem );
314           
315            tc->shouldChangeAnnounce = 0;
316        }
317        else
318        {
319            if( tc->announceTier != 0 )
320            {
321                setAnnounce( tc, inf->trackerAnnounceList );
322                tc->announceTier = 0;
323            }
324            tc->announceTierLast = 0;
325        }
326       
327        if( tr_fdSocketWillCreate( tor->fdlimit, 1 ) )
328        {
329            return 0;
330        }
331        tc->dateTry = tr_date();
332        tc->http = getQuery( tc );
333
334        tr_inf( "Tracker: connecting to %s:%d (%s)",
335                inf->trackerAddress, inf->trackerPort,
336                tc->started ? "sending 'started'" :
337                ( tc->completed ? "sending 'completed'" :
338                  ( tc->stopped ? "sending 'stopped'" :
339                    ( 0 < tc->newPort ? "sending 'stopped' to change port" :
340                      "getting peers" ) ) ) );
341    }
342
343    if( NULL != tc->http )
344    {
345        switch( tr_httpPulse( tc->http, &data, &len ) )
346        {
347            case TR_WAIT:
348                break;
349
350            case TR_ERROR:
351                killHttp( &tc->http, tor->fdlimit );
352                tc->dateTry = tr_date();
353               
354                failureAnnouncing( tc );
355                if ( tc->shouldChangeAnnounce )
356                {
357                    tr_trackerPulse( tc );
358                }
359               
360                return;
361
362            case TR_OK:
363                readAnswer( tc, data, len );
364                killHttp( &tc->http, tor->fdlimit );
365               
366                /* Something happened to need to try next address */
367                if ( tc->shouldChangeAnnounce )
368                {
369                    tr_trackerPulse( tc );
370                    return;
371                }
372               
373                break;
374        }
375    }
376   
377    if( ( NULL == tc->httpScrape ) && shouldScrape( tc ) )
378    {
379        if( tr_fdSocketWillCreate( tor->fdlimit, 1 ) )
380        {
381            return 0;
382        }
383        tc->dateScrape = tr_date();
384        tc->httpScrape = getScrapeQuery( tc );
385        tr_inf( "Scrape: sent http request to %s:%d",
386                    inf->trackerAddress, inf->trackerPort );
387    }
388
389    if( NULL != tc->httpScrape )
390    {
391        switch( tr_httpPulse( tc->httpScrape, &data, &len ) )
392        {
393            case TR_WAIT:
394                break;
395
396            case TR_ERROR:
397                killHttp( &tc->httpScrape, tor->fdlimit );
398                tc->lastScrapeFailed = 1;
399                break;
400
401            case TR_OK:
402                readScrapeAnswer( tc, data, len );
403                killHttp( &tc->httpScrape, tor->fdlimit );
404               
405                break;
406        }
407    }
408
409    return 0;
410}
411
412void tr_trackerCompleted( tr_tracker_t * tc )
413{
414    tc->started   = 0;
415    tc->completed = 1;
416    tc->stopped   = 0;
417}
418
419void tr_trackerStopped( tr_tracker_t * tc )
420{
421    tr_torrent_t * tor = tc->tor;
422
423    /* If we are already sending a query at the moment, we need to
424       reconnect */
425    killHttp( &tc->http, tor->fdlimit );
426
427    tc->started   = 0;
428    tc->completed = 0;
429    tc->stopped   = 1;
430
431    /* Even if we have connected recently, reconnect right now */
432    tc->dateTry = 0;
433}
434
435void tr_trackerClose( tr_tracker_t * tc )
436{
437    tr_torrent_t * tor = tc->tor;
438
439    killHttp( &tc->http, tor->fdlimit );
440    killHttp( &tc->httpScrape, tor->fdlimit );
441    free( tc->trackerid );
442    free( tc );
443}
444
445static tr_http_t * getQuery( tr_tracker_t * tc )
446{
447    tr_torrent_t * tor = tc->tor;
448    tr_info_t    * inf = &tor->info;
449
450    char         * event, * trackerid, * idparam;
451    uint64_t       left;
452    uint64_t       down;
453    uint64_t       up;
454    char           start;
455    int            numwant = 50;
456
457    down = tor->downloadedCur;
458    up   = tor->uploadedCur;
459    if( tc->started )
460    {
461        event = "&event=started";
462        down  = 0;
463        up    = 0;
464
465        if( 0 < tc->newPort )
466        {
467            tc->bindPort = tc->newPort;
468            tc->newPort  = -1;
469        }
470    }
471    else if( tc->completed )
472    {
473        event = "&event=completed";
474    }
475    else if( tc->stopped || 0 < tc->newPort )
476    {
477        event = "&event=stopped";
478        numwant = 0;
479    }
480    else
481    {
482        event = "";
483    }
484
485    if( NULL == tc->trackerid )
486    {
487        trackerid = "";
488        idparam   = "";
489    }
490    else
491    {
492        trackerid = tc->trackerid;
493        idparam   = "&trackerid=";
494    }
495
496    start = ( strchr( inf->trackerAnnounce, '?' ) ? '&' : '?' );
497    left  = tr_cpLeftBytes( tor->completion );
498
499    return tr_httpClient( TR_HTTP_GET, inf->trackerAddress, inf->trackerPort,
500                          "%s%c"
501                          "info_hash=%s&"
502                          "peer_id=%s&"
503                          "port=%d&"
504                          "uploaded=%"PRIu64"&"
505                          "downloaded=%"PRIu64"&"
506                          "left=%"PRIu64"&"
507                          "compact=1&"
508                          "numwant=%d&"
509                          "key=%s"
510                          "%s%s"
511                          "%s",
512                          inf->trackerAnnounce, start, tor->escapedHashString,
513                          tc->id, tc->bindPort, up, down, left, numwant,
514                          tor->key, idparam, trackerid, event );
515}
516
517static tr_http_t * getScrapeQuery( tr_tracker_t * tc )
518{
519    tr_torrent_t * tor = tc->tor;
520    tr_info_t    * inf = &tor->info;
521
522    char           start;
523
524    start = ( strchr( inf->trackerScrape, '?' ) ? '&' : '?' );
525
526    return tr_httpClient( TR_HTTP_GET, inf->trackerAddress, inf->trackerPort,
527                          "%s%c"
528                          "info_hash=%s",
529                          inf->trackerScrape, start, tor->escapedHashString );
530}
531
532static void readAnswer( tr_tracker_t * tc, const char * data, int len )
533{
534    tr_torrent_t * tor = tc->tor;
535    int i;
536    int code;
537    benc_val_t   beAll;
538    benc_val_t * bePeers, * beFoo;
539    const uint8_t * body;
540    int bodylen, shouldfree, scrapeNeeded;
541
542    tc->dateTry = tr_date();
543    code = tr_httpResponseCode( data, len );
544   
545    if( 0 > code )
546    {
547        /* We don't have a valid HTTP status line */
548        tr_inf( "Tracker: invalid HTTP status line" );
549        tc->lastAttempt = TC_ATTEMPT_NOREACH;
550        failureAnnouncing( tc );
551        return;
552    }
553
554    if( !TR_HTTP_STATUS_OK( code ) )
555    {
556        /* we didn't get a 2xx status code */
557        tr_err( "Tracker: invalid HTTP status code: %i", code );
558        tc->lastAttempt = TC_ATTEMPT_ERROR;
559        failureAnnouncing( tc );
560        return;
561    }
562
563    /* find the end of the http headers */
564    body = (uint8_t *) tr_httpParse( data, len, NULL );
565    if( NULL == body )
566    {
567        tr_err( "Tracker: could not find end of HTTP headers" );
568        tc->lastAttempt = TC_ATTEMPT_NOREACH;
569        failureAnnouncing( tc );
570        return;
571    }
572    bodylen = len - ( body - (const uint8_t*)data );
573
574    /* Find and load the dictionary */
575    shouldfree = 0;
576    for( i = 0; i < bodylen; i++ )
577    {
578        if( !tr_bencLoad( &body[i], bodylen - i, &beAll, NULL ) )
579        {
580            shouldfree = 1;
581            break;
582        }
583    }
584
585    if( i >= bodylen )
586    {
587        if( tc->stopped || 0 < tc->newPort )
588        {
589            tc->lastAttempt = TC_ATTEMPT_OK;
590            goto nodict;
591        }
592        tr_err( "Tracker: no valid dictionary found in answer" );
593        tc->lastAttempt = TC_ATTEMPT_ERROR;
594        failureAnnouncing( tc );
595        return;
596    }
597
598    // tr_bencPrint( &beAll );
599
600    if( ( bePeers = tr_bencDictFind( &beAll, "failure reason" ) ) )
601    {
602        tr_err( "Tracker: %s", bePeers->val.s.s );
603        tor->error |= TR_ETRACKER;
604        snprintf( tor->trackerError, sizeof( tor->trackerError ),
605                  "%s", bePeers->val.s.s );
606        tc->lastAttempt = TC_ATTEMPT_ERROR;
607        failureAnnouncing( tc );
608        goto cleanup;
609    }
610
611    tor->error &= ~TR_ETRACKER;
612    tc->lastAttempt = TC_ATTEMPT_OK;
613
614    /* Get the tracker interval, force to between
615       10 sec and 5 mins */
616    beFoo = tr_bencDictFind( &beAll, "interval" );
617    if( !beFoo || TYPE_INT != beFoo->type )
618    {
619        tr_err( "Tracker: no 'interval' field" );
620        goto cleanup;
621    }
622
623    tc->interval = beFoo->val.i;
624    tr_inf( "Tracker: interval = %d seconds", tc->interval );
625
626    tc->interval = MIN( tc->interval, 300 );
627    tc->interval = MAX( 10, tc->interval );
628
629    /* Get the tracker minimum interval, force to between
630       10 sec and 5 mins  */
631    beFoo = tr_bencDictFind( &beAll, "min interval" );
632    if( beFoo && TYPE_INT == beFoo->type )
633    {
634        tc->minInterval = beFoo->val.i;
635        tr_inf( "Tracker: min interval = %d seconds", tc->minInterval );
636
637        tc->minInterval = MIN( tc->minInterval, 300 );
638        tc->minInterval = MAX( 10, tc->minInterval );
639
640        if( tc->interval < tc->minInterval )
641        {
642            tc->interval = tc->minInterval;
643            tr_inf( "Tracker: 'interval' less than 'min interval', "
644                    "using 'min interval'" );
645        }
646    }
647    else
648    {
649        tc->minInterval = 0;
650        tr_inf( "Tracker: no 'min interval' field" );
651    }
652
653    scrapeNeeded = 0;
654    beFoo = tr_bencDictFind( &beAll, "complete" );
655    if( beFoo && TYPE_INT == beFoo->type )
656    {
657        tc->seeders = beFoo->val.i;
658    }
659    else
660    {
661        scrapeNeeded = 1;
662    }
663
664    beFoo = tr_bencDictFind( &beAll, "incomplete" );
665    if( beFoo && TYPE_INT == beFoo->type )
666    {
667        tc->leechers = beFoo->val.i;
668    }
669    else
670    {
671        scrapeNeeded = 1;
672    }
673
674    tc->scrapeNeeded = scrapeNeeded;
675    if( !scrapeNeeded )
676    {
677        tc->hasManyPeers = ( tc->seeders + tc->leechers >= 50 );
678    }
679
680    beFoo = tr_bencDictFind( &beAll, "tracker id" );
681    if( beFoo )
682    {
683        free( tc->trackerid );
684        tc->trackerid = strdup( beFoo->val.s.s );
685        tr_inf( "Tracker: tracker id = %s", tc->trackerid );
686    }
687
688    bePeers = tr_bencDictFind( &beAll, "peers" );
689    if( !bePeers )
690    {
691        if( tc->stopped || 0 < tc->newPort )
692        {
693            goto nodict;
694        }
695        tr_err( "Tracker: no \"peers\" field" );
696        failureAnnouncing( tc );
697        goto cleanup;
698    }
699
700    if( bePeers->type & TYPE_LIST )
701    {
702        char * ip;
703        int    port;
704
705        /* Original protocol */
706        tr_inf( "Tracker: got %d peers", bePeers->val.l.count );
707
708        for( i = 0; i < bePeers->val.l.count; i++ )
709        {
710            beFoo = tr_bencDictFind( &bePeers->val.l.vals[i], "ip" );
711            if( !beFoo )
712                continue;
713            ip = beFoo->val.s.s;
714            beFoo = tr_bencDictFind( &bePeers->val.l.vals[i], "port" );
715            if( !beFoo )
716                continue;
717            port = beFoo->val.i;
718
719            tr_peerAddOld( tor, ip, port );
720        }
721
722        if( bePeers->val.l.count >= 50 )
723        {
724            tc->hasManyPeers = 1;
725        }
726    }
727    else if( bePeers->type & TYPE_STR )
728    {
729        struct in_addr addr;
730        in_port_t      port;
731
732        /* "Compact" extension */
733        if( bePeers->val.s.i % 6 )
734        {
735            tr_err( "Tracker: \"peers\" of size %d",
736                    bePeers->val.s.i );
737            tr_lockUnlock( &tor->lock );
738            goto cleanup;
739        }
740
741        tr_inf( "Tracker: got %d peers", bePeers->val.s.i / 6 );
742        for( i = 0; i < bePeers->val.s.i / 6; i++ )
743        {
744            memcpy( &addr, &bePeers->val.s.s[6*i],   4 );
745            memcpy( &port, &bePeers->val.s.s[6*i+4], 2 );
746
747            tr_peerAddCompact( tor, addr, port );
748        }
749
750        if( bePeers->val.s.i / 6 >= 50 )
751        {
752            tc->hasManyPeers = 1;
753        }
754    }
755
756nodict:
757    /* Success */
758    tc->started   = 0;
759    tc->completed = 0;
760    tc->dateOk    = tr_date();
761
762    if( tc->stopped )
763    {
764        tor->status = TR_STATUS_STOPPED;
765        tc->stopped = 0;
766    }
767    else if( 0 < tc->newPort )
768    {
769        tc->started  = 1;
770    }
771
772cleanup:
773    if( shouldfree )
774    {
775        tr_bencFree( &beAll );
776    }
777}
778
779static void readScrapeAnswer( tr_tracker_t * tc, const char * data, int len )
780{
781    int code;
782    const uint8_t * body;
783    int bodylen, ii;
784    benc_val_t scrape, * val1, * val2;
785
786    code = tr_httpResponseCode( data, len );
787    if( 0 > code )
788    {
789        /* We don't have a valid HTTP status line */
790        tr_inf( "Scrape: invalid HTTP status line" );
791        tc->lastScrapeFailed = 1;
792        return;
793    }
794
795    if( !TR_HTTP_STATUS_OK( code ) )
796    {
797        /* we didn't get a 2xx status code */
798        tr_err( "Scrape: invalid HTTP status code: %i", code );
799        tc->lastScrapeFailed = 1;
800        return;
801    }
802
803    /* find the end of the http headers */
804    body = (uint8_t *) tr_httpParse( data, len, NULL );
805    if( NULL == body )
806    {
807        tr_err( "Scrape: could not find end of HTTP headers" );
808        tc->lastScrapeFailed = 1;
809        return;
810    }
811
812    tc->lastScrapeFailed = 0;
813    bodylen = len - ( body - (const uint8_t*)data );
814
815    for( ii = 0; ii < bodylen; ii++ )
816    {
817        if( !tr_bencLoad( body + ii, bodylen - ii, &scrape, NULL ) )
818        {
819            break;
820        }
821    }
822    if( ii >= bodylen )
823    {
824        return;
825    }
826
827    val1 = tr_bencDictFind( &scrape, "files" );
828    if( !val1 )
829    {
830        tr_bencFree( &scrape );
831        return;
832    }
833    val1 = &val1->val.l.vals[1];
834    if( !val1 )
835    {
836        tr_bencFree( &scrape );
837        return;
838    }
839   
840    val2 = tr_bencDictFind( val1, "complete" );
841    if( !val2 )
842    {
843        tr_bencFree( &scrape );
844        return;
845    }
846    tc->seeders = val2->val.i;
847   
848    val2 = tr_bencDictFind( val1, "incomplete" );
849    if( !val2 )
850    {
851        tr_bencFree( &scrape );
852        return;
853    }
854    tc->leechers = val2->val.i;
855   
856    val2 = tr_bencDictFind( val1, "downloaded" );
857    if( !val2 )
858    {
859        tr_bencFree( &scrape );
860        return;
861    }
862    tc->complete = val2->val.i;
863   
864    val2 = tr_bencDictFind( val1, "flags" );
865    if( val2 )
866    {
867        val2 = tr_bencDictFind( val2, "min_request_interval" );
868        if( val2 )
869        {
870            tc->scrapeInterval = val2->val.i;
871        }
872    }
873   
874    tc->hasManyPeers = ( tc->seeders + tc->leechers >= 50 );
875   
876    tr_bencFree( &scrape );
877}
878
879int tr_trackerSeeders( tr_tracker_t * tc )
880{
881    if( !tc )
882    {
883        return -1;
884    }
885    return tc->seeders;
886}
887
888int tr_trackerLeechers( tr_tracker_t * tc )
889{
890    if( !tc )
891    {
892        return -1;
893    }
894    return tc->leechers;
895}
896
897int tr_trackerDownloaded( tr_tracker_t * tc )
898{
899    if( !tc )
900    {
901        return -1;
902    }
903    return tc->complete;
904}
905
906/* Blocking version */
907int tr_trackerScrape( tr_torrent_t * tor, int * s, int * l, int * d )
908{
909    tr_info_t    * inf = &tor->info;
910   
911    tr_tracker_t * tc;
912    tr_http_t    * http;
913    const char   * data;
914    int            len;
915    int            ret;
916
917    if( !inf->trackerCanScrape )
918    {
919        return 1;
920    }
921
922    tc = tr_trackerInit( tor );
923    http = getScrapeQuery( tc );
924
925    for( data = NULL; !data; tr_wait( 10 ) )
926    {
927        switch( tr_httpPulse( http, &data, &len ) )
928        {
929            case TR_WAIT:
930                break;
931
932            case TR_ERROR:
933                goto scrapeDone;
934
935            case TR_OK:
936                readScrapeAnswer( tc, data, len );
937                goto scrapeDone;
938        }
939    }
940
941scrapeDone:
942    tr_httpClose( http );
943
944    ret = 1;
945    if( tc->seeders > -1 && tc->leechers > -1 && tc->complete > -1 )
946    {
947        *s = tc->seeders;
948        *l = tc->leechers;
949        *d = tc->complete;
950        ret = 0;
951    }
952
953    tr_trackerClose( tc );
954    return ret;
955}
956
957static void killHttp( tr_http_t ** http, tr_fd_t * fdlimit )
958{
959    if( NULL != *http )
960    {
961        tr_httpClose( *http );
962        tr_fdSocketClosed( fdlimit, 1 );
963        *http = NULL;
964    }
965}
Note: See TracBrowser for help on using the repository browser.