source: trunk/libtransmission/torrent.c @ 5994

Last change on this file since 5994 was 5994, checked in by charles, 14 years ago

#981: add tr_stat.dateAdded to libT; add "sort by age" to gtk+ client

  • Property svn:keywords set to Date Rev Author Id
File size: 40.0 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 5994 2008-06-02 04:41:55Z charles $
3 *
4 * Copyright (c) 2005-2008 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 <sys/types.h> /* stat */
26#include <sys/stat.h> /* stat */
27#include <unistd.h> /* stat */
28
29#include <assert.h>
30#include <limits.h> /* INT_MAX */
31#include <string.h> /* memcmp */
32#include <stdlib.h> /* qsort */
33
34#include "transmission.h"
35#include "bencode.h"
36#include "completion.h"
37#include "crypto.h" /* for tr_sha1 */
38#include "resume.h"
39#include "fdlimit.h" /* tr_fdFileClose */
40#include "metainfo.h"
41#include "peer-mgr.h"
42#include "ratecontrol.h"
43#include "torrent.h"
44#include "tracker.h"
45#include "trevent.h"
46#include "utils.h"
47#include "verify.h"
48
49#define MAX_BLOCK_SIZE (1024*16)
50
51/***
52****
53***/
54
55int
56tr_torrentId( const tr_torrent * tor )
57{
58    return tor->uniqueId;
59}
60
61tr_torrent*
62tr_torrentFindFromId( tr_handle * handle, int id )
63{
64    tr_torrent * tor = NULL;
65
66    while(( tor = tr_torrentNext( handle, tor )))
67        if( tor->uniqueId == id )
68            return tor;
69
70    return NULL;
71}
72
73tr_torrent*
74tr_torrentFindFromHashString( tr_handle * handle, const char * str )
75{
76    tr_torrent * tor = NULL;
77
78    while(( tor = tr_torrentNext( handle, tor )))
79        if( !strcmp( str, tor->info.hashString ) )
80            return tor;
81
82    return NULL;
83}
84
85int
86tr_torrentExists( const tr_handle * handle,
87                  const uint8_t   * torrentHash )
88{
89    return tr_torrentFindFromHash( (tr_handle*)handle, torrentHash ) != NULL;
90}
91
92tr_torrent*
93tr_torrentFindFromHash( tr_handle      * handle,
94                        const uint8_t  * torrentHash )
95{
96    tr_torrent * tor = NULL;
97
98    while(( tor = tr_torrentNext( handle, tor )))
99        if( !memcmp( tor->info.hash, torrentHash, SHA_DIGEST_LENGTH ) )
100            return tor;
101
102    return NULL;
103}
104
105tr_torrent*
106tr_torrentFindFromObfuscatedHash( tr_handle      * handle,
107                                  const uint8_t  * obfuscatedTorrentHash )
108{
109    tr_torrent * tor = NULL;
110
111    while(( tor = tr_torrentNext( handle, tor )))
112        if( !memcmp( tor->obfuscatedHash, obfuscatedTorrentHash, SHA_DIGEST_LENGTH ) )
113            return tor;
114
115    return NULL;
116}
117
118/***
119****  LOCKS
120***/
121
122void
123tr_torrentLock( const tr_torrent * tor )
124{
125    tr_globalLock( tor->handle );
126}
127
128void
129tr_torrentUnlock( const tr_torrent * tor )
130{
131    tr_globalUnlock( tor->handle );
132}
133
134/***
135****  PER-TORRENT UL / DL SPEEDS
136***/
137
138void
139tr_torrentSetSpeedMode( tr_torrent   * tor,
140                        int            up_or_down,
141                        tr_speedlimit  mode )
142{
143    tr_speedlimit * limit = up_or_down==TR_UP
144        ? &tor->uploadLimitMode
145        : &tor->downloadLimitMode;
146    *limit = mode;
147}
148
149tr_speedlimit
150tr_torrentGetSpeedMode( const tr_torrent * tor,
151                        int                up_or_down)
152{
153    return up_or_down==TR_UP ? tor->uploadLimitMode
154                             : tor->downloadLimitMode;
155}
156
157void
158tr_torrentSetSpeedLimit( tr_torrent   * tor,
159                         int            up_or_down,
160                         int            single_KiB_sec )
161{
162    tr_ratecontrol * rc = up_or_down==TR_UP ? tor->upload : tor->download;
163    tr_rcSetLimit( rc, single_KiB_sec );
164}
165
166int
167tr_torrentGetSpeedLimit( const tr_torrent  * tor,
168                         int                 up_or_down )
169{
170    tr_ratecontrol * rc = up_or_down==TR_UP ? tor->upload : tor->download;
171    return tr_rcGetLimit( rc );
172}
173
174/***
175****
176***/
177
178static void
179onTrackerResponse( void * tracker UNUSED, void * vevent, void * user_data )
180{
181    tr_torrent * tor = user_data;
182    tr_tracker_event * event = vevent;
183
184    switch( event->messageType )
185    {
186        case TR_TRACKER_PEERS: {
187            size_t i, n;
188            tr_pex * pex = tr_peerMgrCompactToPex( event->compact,
189                                                   event->compactLen,
190                                                   NULL, &n );
191            if( event->allAreSeeds )
192                tr_tordbg( tor, "Got %d seeds from tracker", (int)n );
193            else
194                tr_torinf( tor, _( "Got %d peers from tracker" ), (int)n );
195
196            for( i=0; i<n; ++i ) {
197                if( event->allAreSeeds )
198                    pex[i].flags |= ADDED_F_SEED_FLAG;
199                tr_peerMgrAddPex( tor->handle->peerMgr, tor->info.hash,
200                                  TR_PEER_FROM_TRACKER, pex+i );
201            }
202
203            tr_free( pex );
204            break;
205        }
206
207        case TR_TRACKER_WARNING:
208            tr_torerr( tor, _( "Tracker warning: \"%s\"" ), event->text );
209            tor->error = TR_ERROR_TC_WARNING;
210            tr_strlcpy( tor->errorString, event->text, sizeof(tor->errorString) );
211            break;
212
213        case TR_TRACKER_ERROR:
214            tr_torerr( tor, _( "Tracker error: \"%s\"" ), event->text );
215            tor->error = TR_ERROR_TC_ERROR;
216            tr_strlcpy( tor->errorString, event->text, sizeof(tor->errorString) );
217            break;
218
219        case TR_TRACKER_ERROR_CLEAR:
220            tor->error = 0;
221            tor->errorString[0] = '\0';
222            break;
223    }
224}
225
226/***
227****
228****  TORRENT INSTANTIATION
229****
230***/
231
232static int
233getBytePiece( const tr_info * info, uint64_t byteOffset )
234{
235    assert( info != NULL );
236    assert( info->pieceSize != 0 );
237
238    return byteOffset / info->pieceSize;
239}
240
241static void
242initFilePieces ( tr_info * info, tr_file_index_t fileIndex )
243{
244    tr_file * file = &info->files[fileIndex];
245    uint64_t firstByte, lastByte;
246
247    assert( info != NULL );
248    assert( fileIndex < info->fileCount );
249
250    file = &info->files[fileIndex];
251    firstByte = file->offset;
252    lastByte = firstByte + (file->length ? file->length-1 : 0);
253    file->firstPiece = getBytePiece( info, firstByte );
254    file->lastPiece = getBytePiece( info, lastByte );
255}
256
257static int
258pieceHasFile( tr_piece_index_t piece, const tr_file * file )
259{
260    return ( file->firstPiece <= piece ) && ( piece <= file->lastPiece );
261}
262
263static tr_priority_t
264calculatePiecePriority ( const tr_torrent * tor,
265                         tr_piece_index_t   piece,
266                         int                fileHint )
267{
268    tr_file_index_t i;
269    int priority = TR_PRI_LOW;
270
271    /* find the first file that has data in this piece */
272    if( fileHint >= 0 ) {
273        i = fileHint;
274        while( i>0 && pieceHasFile( piece, &tor->info.files[i-1] ) )
275            --i;
276    } else {
277        for( i=0; i<tor->info.fileCount; ++i )
278            if( pieceHasFile( piece, &tor->info.files[i] ) )
279                break;
280    }
281
282    /* the piece's priority is the max of the priorities
283     * of all the files in that piece */
284    for( ; i<tor->info.fileCount; ++i )
285    {
286        const tr_file * file = &tor->info.files[i];
287
288        if( !pieceHasFile( piece, file ) )
289            break;
290
291        priority = MAX( priority, file->priority );
292
293        /* when dealing with multimedia files, getting the first and
294           last pieces can sometimes allow you to preview it a bit
295           before it's fully downloaded... */
296        if ( file->priority >= TR_PRI_NORMAL )
297            if ( file->firstPiece == piece || file->lastPiece == piece )
298                priority = TR_PRI_HIGH;
299    }
300
301    return priority;
302}
303
304static void
305tr_torrentInitFilePieces( tr_torrent * tor )
306{
307    tr_file_index_t ff;
308    tr_piece_index_t pp;
309    uint64_t offset = 0;
310    tr_info * inf = &tor->info;
311
312    assert( inf != NULL );
313
314    for( ff=0; ff<inf->fileCount; ++ff ) {
315      inf->files[ff].offset = offset;
316      offset += inf->files[ff].length;
317      initFilePieces( inf, ff );
318    }
319
320    for( pp=0; pp<inf->pieceCount; ++pp )
321        inf->pieces[pp].priority = calculatePiecePriority( tor, pp, -1 );
322}
323
324int
325tr_torrentPromoteTracker( tr_torrent * tor, int pos )
326{
327    int i;
328    int tier;
329
330    assert( tor != NULL );
331    assert( ( 0 <= pos ) && ( pos < tor->info.trackerCount ) );
332
333    /* the tier of the tracker we're promoting */
334    tier = tor->info.trackers[pos].tier;
335
336    /* find the index of that tier's first tracker */
337    for( i=0; i<tor->info.trackerCount; ++i )
338        if( tor->info.trackers[i].tier == tier )
339            break;
340
341    assert( i < tor->info.trackerCount );
342
343    /* promote the tracker at `pos' to the front of the tier */
344    if( i != pos ) {
345        const tr_tracker_info tmp = tor->info.trackers[i];
346        tor->info.trackers[i] = tor->info.trackers[pos];
347        tor->info.trackers[pos] = tmp;
348    }
349
350    /* return the new position of the tracker that started out at [pos] */
351    return i;
352}
353
354struct RandomTracker
355{
356    tr_tracker_info tracker;
357    int random_value;
358};
359
360/* the tiers will be sorted from lowest to highest,
361 * and trackers are randomized within the tiers */
362static int
363compareRandomTracker( const void * va, const void * vb )
364{
365    const struct RandomTracker * a = va;
366    const struct RandomTracker * b = vb;
367
368    if( a->tracker.tier != b->tracker.tier )
369        return a->tracker.tier - b->tracker.tier;
370
371   return a->random_value - b->random_value;
372}
373   
374static void
375randomizeTiers( tr_info * info )
376{
377    int i;
378    const int n = info->trackerCount;
379    struct RandomTracker * r = tr_new0( struct RandomTracker, n );
380    for( i=0; i<n; ++i ) {
381        r[i].tracker = info->trackers[i];
382        r[i].random_value = tr_rand( INT_MAX );
383    }
384    qsort( r, n, sizeof( struct RandomTracker ), compareRandomTracker );
385    for( i=0; i<n; ++i )
386        info->trackers[i] = r[i].tracker;
387    tr_free( r );
388}
389
390static void torrentStart( tr_torrent * tor, int reloadProgress );
391
392static void
393torrentRealInit( tr_handle     * h,
394                 tr_torrent    * tor,
395                 const tr_ctor * ctor )
396{
397    int doStart;
398    uint64_t loaded;
399    uint64_t t;
400    tr_info * info = &tor->info;
401    static int nextUniqueId = 1;
402   
403    tr_globalLock( h );
404
405    tor->handle   = h;
406    tor->uniqueId = nextUniqueId++;
407
408    randomizeTiers( info );
409
410    /**
411     * Decide on a block size.  constraints:
412     * (1) most clients decline requests over 16 KiB
413     * (2) pieceSize must be a multiple of block size
414     */
415    tor->blockSize = info->pieceSize;
416    while( tor->blockSize > MAX_BLOCK_SIZE )
417        tor->blockSize /= 2;
418
419    tor->lastPieceSize = info->totalSize % info->pieceSize;
420
421    if( !tor->lastPieceSize )
422         tor->lastPieceSize = info->pieceSize;
423
424    tor->lastBlockSize = info->totalSize % tor->blockSize;
425
426    if( !tor->lastBlockSize )
427         tor->lastBlockSize = tor->blockSize;
428
429    tor->blockCount =
430        ( info->totalSize + tor->blockSize - 1 ) / tor->blockSize;
431
432    tor->blockCountInPiece =
433        info->pieceSize / tor->blockSize;
434
435    tor->blockCountInLastPiece =
436        ( tor->lastPieceSize + tor->blockSize - 1 ) / tor->blockSize;
437
438    /* check our work */
439    assert( ( info->pieceSize % tor->blockSize ) == 0 );
440    t = info->pieceCount - 1;
441    t *= info->pieceSize;
442    t += tor->lastPieceSize;
443    assert( t == info->totalSize );
444    t = tor->blockCount - 1;
445    t *= tor->blockSize;
446    t += tor->lastBlockSize;
447    assert( t == info->totalSize );
448    t = info->pieceCount - 1;
449    t *= tor->blockCountInPiece;
450    t += tor->blockCountInLastPiece;
451    assert( t == (uint64_t)tor->blockCount );
452
453    tor->completion = tr_cpInit( tor );
454
455    tr_torrentInitFilePieces( tor );
456
457    tor->upload         = tr_rcInit();
458    tor->download       = tr_rcInit();
459    tor->swarmSpeed     = tr_rcInit();
460
461    tr_sha1( tor->obfuscatedHash, "req2", 4,
462                                  info->hash, SHA_DIGEST_LENGTH,
463                                  NULL );
464
465    tr_peerMgrAddTorrent( h->peerMgr, tor );
466
467    if( !h->isPortSet )
468        tr_sessionSetPeerPort( h, TR_DEFAULT_PORT );
469
470    assert( !tor->downloadedCur );
471    assert( !tor->uploadedCur );
472
473    tor->error   = TR_OK;
474
475    tor->checkedPieces = tr_bitfieldNew( tor->info.pieceCount );
476    tr_torrentUncheck( tor );
477
478    tor->addedDate = time( NULL ); /* this is a default value to be
479                                      overwritten by the resume file */
480    loaded = tr_torrentLoadResume( tor, ~0, ctor );
481   
482    doStart = tor->isRunning;
483    tor->isRunning = 0;
484
485    if( !(loaded & TR_FR_SPEEDLIMIT ) ) {
486        tr_torrentSetSpeedLimit( tor, TR_UP,
487                tr_sessionGetSpeedLimit( tor->handle, TR_UP ) );
488        tr_torrentSetSpeedLimit( tor, TR_DOWN,
489                tr_sessionGetSpeedLimit( tor->handle, TR_DOWN ) );
490    }
491
492    tor->cpStatus = tr_cpGetStatus( tor->completion );
493
494    tor->tracker = tr_trackerNew( tor );
495    tor->trackerSubscription = tr_trackerSubscribe( tor->tracker, onTrackerResponse, tor );
496
497    {
498        tr_torrent * it = NULL;
499        tr_torrent * last = NULL;
500        while(( it = tr_torrentNext( h, it )))
501            last = it;
502        if( !last )
503            h->torrentList = tor;
504        else
505            last->next = tor;
506        ++h->torrentCount;
507    }
508
509    tr_globalUnlock( h );
510
511    /* maybe save our own copy of the metainfo */
512    if( tr_ctorGetSave( ctor ) ) {
513        const tr_benc * val;
514        if( !tr_ctorGetMetainfo( ctor, &val ) ) {
515            const char * filename = tor->info.torrent;
516            tr_bencSaveFile( filename, val );
517            tr_sessionSetTorrentFile( tor->handle, tor->info.hashString, filename );
518        }
519    }
520
521    tr_metainfoMigrate( h, &tor->info );
522
523    if( doStart )
524        torrentStart( tor, FALSE );
525}
526
527int
528tr_torrentParse( const tr_handle  * handle,
529                 const tr_ctor    * ctor,
530                 tr_info          * setmeInfo )
531{
532    int err = 0;
533    int doFree;
534    tr_info tmp;
535    const tr_benc * metainfo;
536
537    if( setmeInfo == NULL )
538        setmeInfo = &tmp;
539    memset( setmeInfo, 0, sizeof( tr_info ) );
540
541    if( !err && tr_ctorGetMetainfo( ctor, &metainfo ) )
542        return TR_EINVALID;
543
544    err = tr_metainfoParse( handle, setmeInfo, metainfo );
545    doFree = !err && ( setmeInfo == &tmp );
546
547    if( !err && tr_torrentExists( handle, setmeInfo->hash ) )
548        err = TR_EDUPLICATE;
549
550    if( doFree )
551        tr_metainfoFree( setmeInfo );
552
553    return err;
554}
555
556tr_torrent *
557tr_torrentNew( tr_handle      * handle,
558               const tr_ctor  * ctor,
559               int            * setmeError )
560{
561    int err;
562    tr_info tmpInfo;
563    tr_torrent * tor = NULL;
564
565    err = tr_torrentParse( handle, ctor, &tmpInfo );
566    if( !err ) {
567        tor = tr_new0( tr_torrent, 1 );
568        tor->info = tmpInfo;
569        torrentRealInit( handle, tor, ctor );
570    } else if( setmeError ) {
571        *setmeError = err;
572    }
573
574    return tor;
575}
576
577/**
578***
579**/
580
581void
582tr_torrentSetDownloadDir( tr_torrent * tor, const char * path )
583{
584    if( !path || !tor->downloadDir || strcmp( path, tor->downloadDir ) )
585    {
586        tr_free( tor->downloadDir );
587        tor->downloadDir = tr_strdup( path );
588        tr_torrentSaveResume( tor );
589    }
590}
591
592const char*
593tr_torrentGetDownloadDir( const tr_torrent * tor )
594{
595    return tor->downloadDir;
596}
597
598void
599tr_torrentChangeMyPort( tr_torrent * tor )
600{
601    if( tor->tracker )
602        tr_trackerChangeMyPort( tor->tracker );
603}
604
605int
606tr_torrentIsPrivate( const tr_torrent * tor )
607{
608    return tor
609        && tor->info.isPrivate;
610}
611
612int
613tr_torrentAllowsPex( const tr_torrent * tor )
614{
615    return tor
616        && tor->handle->isPexEnabled
617        && !tr_torrentIsPrivate( tor );
618}
619
620static void
621tr_torrentManualUpdateImpl( void * vtor )
622{
623    tr_torrent * tor = vtor;
624    if( tor->isRunning )
625        tr_trackerReannounce( tor->tracker );
626}
627void
628tr_torrentManualUpdate( tr_torrent * tor )
629{
630    tr_runInEventThread( tor->handle, tr_torrentManualUpdateImpl, tor );
631}
632int
633tr_torrentCanManualUpdate( const tr_torrent * tor )
634{
635    return ( tor != NULL )
636        && ( tor->isRunning )
637        && ( tr_trackerCanManualAnnounce( tor->tracker ) );
638}
639
640/* rcRate's averaging code can make it appear that we're
641 * still sending bytes after a torrent stops or all the
642 * peers disconnect, so short-circuit that appearance here */
643void
644tr_torrentGetRates( const tr_torrent * tor,
645                    float            * toClient,
646                    float            * toPeer)
647{
648    const int showSpeed = tor->isRunning
649        && tr_peerMgrHasConnections( tor->handle->peerMgr, tor->info.hash );
650
651    if( toClient )
652        *toClient = showSpeed ? tr_rcRate( tor->download ) : 0.0;
653    if( toPeer )
654        *toPeer = showSpeed ? tr_rcRate( tor->upload ) : 0.0;
655}
656const tr_info *
657tr_torrentInfo( const tr_torrent * tor )
658{
659    return tor ? &tor->info : NULL;
660}
661
662const tr_stat *
663tr_torrentStatCached( tr_torrent * tor )
664{
665    const time_t now = time( NULL );
666
667    return tor && ( now == tor->lastStatTime ) ? &tor->stats
668                                               : tr_torrentStat( tor );
669}
670
671tr_torrent_status
672tr_torrentGetStatus( tr_torrent * tor )
673{
674    tr_torrentRecheckCompleteness( tor );
675
676    if( tor->verifyState == TR_VERIFY_NOW )
677        return TR_STATUS_CHECK;
678    if( tor->verifyState == TR_VERIFY_WAIT )
679        return TR_STATUS_CHECK_WAIT;
680    if( !tor->isRunning )
681        return TR_STATUS_STOPPED;
682    if( tor->cpStatus == TR_CP_INCOMPLETE )
683        return TR_STATUS_DOWNLOAD;
684
685    return TR_STATUS_SEED;
686}
687
688const tr_stat *
689tr_torrentStat( tr_torrent * tor )
690{
691    tr_stat * s;
692    struct tr_tracker * tc;
693    const tr_tracker_info * ti;
694    int seedsConnected = 0;
695
696    if( !tor )
697        return NULL;
698
699    tr_torrentLock( tor );
700
701    tor->lastStatTime = time( NULL );
702
703    s = &tor->stats;
704    s->id = tor->uniqueId;
705    s->status = tr_torrentGetStatus( tor );
706    s->error  = tor->error;
707    memcpy( s->errorString, tor->errorString,
708            sizeof( s->errorString ) );
709
710    tc = tor->tracker;
711    ti = tr_trackerGetAddress( tor->tracker );
712    s->announceURL = ti ? ti->announce : NULL;
713    s->scrapeURL   = ti ? ti->scrape   : NULL;
714    tr_trackerStat( tc, s );
715    tr_trackerGetCounts( tc, &s->completedFromTracker,
716                             &s->leechers, 
717                             &s->seeders );
718    tr_peerMgrTorrentStats( tor->handle->peerMgr,
719                            tor->info.hash,
720                            &s->peersKnown,
721                            &s->peersConnected,
722                               &seedsConnected,
723                            &s->peersSendingToUs,
724                            &s->peersGettingFromUs,
725                             s->peersFrom );
726
727    s->percentComplete = tr_cpPercentComplete ( tor->completion );
728
729    s->percentDone = tr_cpPercentDone( tor->completion );
730    s->leftUntilDone = tr_cpLeftUntilDone( tor->completion );
731    s->sizeWhenDone = tr_cpSizeWhenDone( tor->completion );
732
733    s->recheckProgress =
734        1.0 - (tr_torrentCountUncheckedPieces( tor ) / (double) tor->info.pieceCount);
735
736    tr_torrentGetRates( tor, &s->rateDownload, &s->rateUpload );
737
738    s->swarmSpeed = tr_rcRate( tor->swarmSpeed );
739   
740    s->startDate = tor->startDate;
741    s->activityDate = tor->activityDate;
742    s->addedDate = tor->addedDate;
743
744    s->corruptEver     = tor->corruptCur    + tor->corruptPrev;
745    s->downloadedEver  = tor->downloadedCur + tor->downloadedPrev;
746    s->uploadedEver    = tor->uploadedCur   + tor->uploadedPrev;
747    s->haveValid       = tr_cpHaveValid( tor->completion );
748    s->haveUnchecked   = tr_cpHaveTotal( tor->completion ) - s->haveValid;
749
750
751    if( seedsConnected > 0 )
752    {
753        s->desiredAvailable = s->leftUntilDone;
754    }
755    else if( !s->leftUntilDone || !s->peersConnected )
756    {
757        s->desiredAvailable = 0;
758    }
759    else
760    {
761        tr_piece_index_t i;
762        tr_bitfield * peerPieces = tr_peerMgrGetAvailable( tor->handle->peerMgr,
763                                                           tor->info.hash );
764        s->desiredAvailable = 0;
765        for( i=0; i<tor->info.pieceCount; ++i )
766            if( !tor->info.pieces[i].dnd && tr_bitfieldHas( peerPieces, i ) )
767                s->desiredAvailable += tr_cpMissingBlocksInPiece( tor->completion, i );
768        s->desiredAvailable *= tor->blockSize;
769        tr_bitfieldFree( peerPieces );
770    }
771
772    if( s->leftUntilDone > s->desiredAvailable )
773        s->eta = TR_ETA_NOT_AVAIL;
774    else if( s->rateDownload < 0.1 )
775        s->eta = TR_ETA_UNKNOWN;
776    else
777        s->eta = s->leftUntilDone / s->rateDownload / 1024.0;
778
779    s->ratio = tr_getRatio( s->uploadedEver,
780                            s->downloadedEver ? s->downloadedEver : s->haveValid );
781   
782    tr_torrentUnlock( tor );
783
784    return s;
785}
786
787/***
788****
789***/
790
791static uint64_t
792fileBytesCompleted ( const tr_torrent * tor, tr_file_index_t fileIndex )
793{
794    const tr_file * file     =  &tor->info.files[fileIndex];
795    const tr_block_index_t firstBlock       =  file->offset / tor->blockSize;
796    const uint64_t firstBlockOffset =  file->offset % tor->blockSize;
797    const uint64_t lastOffset       =  file->length ? (file->length-1) : 0;
798    const tr_block_index_t lastBlock        = (file->offset + lastOffset) / tor->blockSize;
799    const uint64_t lastBlockOffset  = (file->offset + lastOffset) % tor->blockSize;
800    uint64_t haveBytes = 0;
801
802    assert( tor != NULL );
803    assert( fileIndex < tor->info.fileCount );
804    assert( file->offset + file->length <= tor->info.totalSize );
805    assert( ( firstBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
806    assert( ( lastBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
807    assert( firstBlock <= lastBlock );
808    assert( tr_torBlockPiece( tor, firstBlock ) == file->firstPiece );
809    assert( tr_torBlockPiece( tor, lastBlock ) == file->lastPiece );
810
811    if( firstBlock == lastBlock )
812    {
813        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
814            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
815    }
816    else
817    {
818        tr_block_index_t i;
819
820        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
821            haveBytes += tor->blockSize - firstBlockOffset;
822
823        for( i=firstBlock+1; i<lastBlock; ++i )
824            if( tr_cpBlockIsComplete( tor->completion, i ) )
825               haveBytes += tor->blockSize;
826
827        if( tr_cpBlockIsComplete( tor->completion, lastBlock ) )
828            haveBytes += lastBlockOffset + 1;
829    }
830
831    return haveBytes;
832}
833
834tr_file_stat *
835tr_torrentFiles( const tr_torrent * tor, tr_file_index_t * fileCount )
836{
837    tr_file_index_t i;
838    const tr_file_index_t n = tor->info.fileCount;
839    tr_file_stat * files = tr_new0( tr_file_stat, n );
840    tr_file_stat * walk = files;
841
842    for( i=0; i<n; ++i, ++walk ) {
843        const uint64_t b = fileBytesCompleted( tor, i );
844        walk->bytesCompleted = b;
845        walk->progress = tr_getRatio( b, tor->info.files[i].length );
846    }
847
848    if( fileCount )
849        *fileCount = n;
850
851    return files;
852}
853
854void
855tr_torrentFilesFree( tr_file_stat * files, tr_file_index_t fileCount UNUSED )
856{
857    tr_free( files );
858}
859
860/***
861****
862***/
863
864tr_peer_stat *
865tr_torrentPeers( const tr_torrent * tor, int * peerCount )
866{
867    tr_peer_stat * ret = NULL;
868
869    if( tor != NULL )
870        ret = tr_peerMgrPeerStats( tor->handle->peerMgr,
871                                   tor->info.hash, peerCount );
872
873    return ret;
874}
875
876void
877tr_torrentPeersFree( tr_peer_stat * peers, int peerCount UNUSED )
878{
879    tr_free( peers );
880}
881
882void
883tr_torrentAvailability( const tr_torrent * tor, int8_t * tab, int size )
884{
885    tr_peerMgrTorrentAvailability( tor->handle->peerMgr,
886                                   tor->info.hash,
887                                   tab, size );
888}
889
890void
891tr_torrentAmountFinished( const tr_torrent * tor, float * tab, int size )
892{
893    tr_torrentLock( tor );
894    tr_cpGetAmountDone( tor->completion, tab, size );
895    tr_torrentUnlock( tor );
896}
897
898void
899tr_torrentResetTransferStats( tr_torrent * tor )
900{
901    tr_torrentLock( tor );
902
903    tor->downloadedPrev += tor->downloadedCur;
904    tor->downloadedCur   = 0;
905    tor->uploadedPrev   += tor->uploadedCur;
906    tor->uploadedCur     = 0;
907    tor->corruptPrev    += tor->corruptCur;
908    tor->corruptCur      = 0;
909
910    tr_torrentUnlock( tor );
911}
912
913
914void
915tr_torrentSetHasPiece( tr_torrent * tor, tr_piece_index_t pieceIndex, int has )
916{
917    tr_torrentLock( tor );
918
919    assert( tor != NULL );
920    assert( pieceIndex < tor->info.pieceCount );
921
922    if( has )
923        tr_cpPieceAdd( tor->completion, pieceIndex );
924    else
925        tr_cpPieceRem( tor->completion, pieceIndex );
926
927    tr_torrentUnlock( tor );
928}
929
930/***
931****
932***/
933
934static void
935freeTorrent( tr_torrent * tor )
936{
937    tr_torrent * t;
938    tr_handle * h = tor->handle;
939    tr_info * inf = &tor->info;
940
941    assert( tor != NULL );
942    assert( !tor->isRunning );
943
944    tr_globalLock( h );
945
946    tr_peerMgrRemoveTorrent( h->peerMgr, tor->info.hash );
947
948    tr_cpClose( tor->completion );
949
950    tr_rcClose( tor->upload );
951    tr_rcClose( tor->download );
952    tr_rcClose( tor->swarmSpeed );
953
954    tr_trackerUnsubscribe( tor->tracker, tor->trackerSubscription );
955    tr_trackerFree( tor->tracker );
956    tor->tracker = NULL;
957
958    tr_bitfieldFree( tor->checkedPieces );
959
960    tr_free( tor->downloadDir );
961
962    if( tor == h->torrentList )
963        h->torrentList = tor->next;
964    else for( t=h->torrentList; t!=NULL; t=t->next ) {
965        if( t->next == tor ) {
966            t->next = tor->next;
967            break;
968        }
969    }
970
971    assert( h->torrentCount >= 1 );
972    h->torrentCount--;
973
974    tr_metainfoFree( inf );
975    tr_free( tor );
976
977    tr_globalUnlock( h );
978}
979
980/**
981***  Start/Stop Callback
982**/
983
984static void
985checkAndStartImpl( void * vtor )
986{
987    tr_torrent * tor = vtor;
988
989    tr_globalLock( tor->handle );
990
991    tor->isRunning  = 1;
992    *tor->errorString = '\0';
993    tr_torrentResetTransferStats( tor );
994    tor->cpStatus = tr_cpGetStatus( tor->completion );
995    tr_torrentSaveResume( tor );
996    tor->startDate = time( NULL );
997    tr_trackerStart( tor->tracker );
998    tr_peerMgrStartTorrent( tor->handle->peerMgr, tor->info.hash );
999
1000    tr_globalUnlock( tor->handle );
1001}
1002
1003static void
1004checkAndStartCB( tr_torrent * tor )
1005{
1006    tr_runInEventThread( tor->handle, checkAndStartImpl, tor );
1007}
1008
1009static void
1010torrentStart( tr_torrent * tor, int reloadProgress )
1011{
1012    tr_globalLock( tor->handle );
1013
1014    if( !tor->isRunning )
1015    {
1016        if( reloadProgress )
1017            tr_torrentLoadResume( tor, TR_FR_PROGRESS, NULL );
1018        tor->isRunning = 1;
1019        tr_verifyAdd( tor, checkAndStartCB );
1020    }
1021
1022    tr_globalUnlock( tor->handle );
1023}
1024
1025void
1026tr_torrentStart( tr_torrent * tor )
1027{
1028    torrentStart( tor, TRUE );
1029}
1030
1031static void
1032torrentRecheckDoneImpl( void * vtor )
1033{
1034    tr_torrentRecheckCompleteness( vtor );
1035}
1036static void
1037torrentRecheckDoneCB( tr_torrent * tor )
1038{
1039    tr_runInEventThread( tor->handle, torrentRecheckDoneImpl, tor );
1040}
1041void
1042tr_torrentVerify( tr_torrent * tor )
1043{
1044    tr_verifyRemove( tor );
1045
1046    tr_globalLock( tor->handle );
1047
1048    tr_torrentUncheck( tor );
1049    tr_verifyAdd( tor, torrentRecheckDoneCB );
1050
1051    tr_globalUnlock( tor->handle );
1052}
1053
1054
1055static void
1056stopTorrent( void * vtor )
1057{
1058    tr_file_index_t i;
1059
1060    tr_torrent * tor = vtor;
1061    tr_verifyRemove( tor );
1062    tr_peerMgrStopTorrent( tor->handle->peerMgr, tor->info.hash );
1063    tr_trackerStop( tor->tracker );
1064
1065    for( i=0; i<tor->info.fileCount; ++i )
1066    {
1067        char path[MAX_PATH_LENGTH];
1068        const tr_file * file = &tor->info.files[i];
1069        tr_buildPath( path, sizeof(path), tor->downloadDir, file->name, NULL );
1070        tr_fdFileClose( path );
1071    }
1072}
1073
1074void
1075tr_torrentStop( tr_torrent * tor )
1076{
1077    tr_globalLock( tor->handle );
1078
1079    tor->isRunning = 0;
1080    if( !tor->isDeleting )
1081        tr_torrentSaveResume( tor );
1082    tr_runInEventThread( tor->handle, stopTorrent, tor );
1083
1084    tr_globalUnlock( tor->handle );
1085}
1086
1087static void
1088closeTorrent( void * vtor )
1089{
1090    tr_torrent * tor = vtor;
1091    tr_torrentSaveResume( tor );
1092    tor->isRunning = 0;
1093    stopTorrent( tor );
1094    if( tor->isDeleting ) {
1095        tr_metainfoRemoveSaved( tor->handle, &tor->info );
1096        tr_torrentRemoveResume( tor );
1097    }
1098    freeTorrent( tor );
1099}
1100
1101void
1102tr_torrentFree( tr_torrent * tor )
1103{
1104    if( tor != NULL )
1105    {
1106        tr_handle * handle = tor->handle;
1107        tr_globalLock( handle );
1108
1109        tr_torrentClearStatusCallback( tor );
1110        tr_runInEventThread( handle, closeTorrent, tor );
1111
1112        tr_globalUnlock( handle );
1113    }
1114}
1115
1116void
1117tr_torrentRemove( tr_torrent * tor )
1118{
1119    tor->isDeleting = 1;
1120    tr_torrentFree( tor );
1121}
1122
1123
1124/**
1125***  Completeness
1126**/
1127
1128static const char *
1129getCompletionString( int type )
1130{
1131    switch( type )
1132    {
1133        /* Translators: this is a minor point that's safe to skip over, but FYI:
1134           "Complete" and "Done" are specific, different terms in Transmission:
1135           "Complete" means we've downloaded every file in the torrent.
1136           "Done" means we're done downloading the files we wanted, but NOT all that exist */
1137        case TR_CP_DONE:     return _( "Done" );
1138        case TR_CP_COMPLETE: return _( "Complete" );
1139        default:             return _( "Incomplete" );
1140    }
1141}
1142
1143static void
1144fireStatusChange( tr_torrent * tor, cp_status_t status )
1145{
1146    assert( tor != NULL );
1147    assert( status==TR_CP_INCOMPLETE || status==TR_CP_DONE || status==TR_CP_COMPLETE );
1148
1149    if( tor->status_func != NULL )
1150        (tor->status_func)( tor, status, tor->status_func_user_data );
1151}
1152
1153void
1154tr_torrentSetStatusCallback( tr_torrent             * tor,
1155                             tr_torrent_status_func   func,
1156                             void                   * user_data )
1157{
1158    assert( tor != NULL );
1159    tor->status_func = func;
1160    tor->status_func_user_data = user_data;
1161}
1162
1163void
1164tr_torrentClearStatusCallback( tr_torrent * torrent )
1165{
1166    tr_torrentSetStatusCallback( torrent, NULL, NULL );
1167}
1168
1169void
1170tr_torrentRecheckCompleteness( tr_torrent * tor )
1171{
1172    cp_status_t cpStatus;
1173
1174    tr_torrentLock( tor );
1175
1176    cpStatus = tr_cpGetStatus( tor->completion );
1177
1178    if( cpStatus != tor->cpStatus )
1179    {
1180        const int recentChange = tor->downloadedCur != 0;
1181
1182        if( recentChange )
1183        {
1184            tr_torinf( tor, _( "State changed from \"%1$s\" to \"%2$s\"" ),
1185                            getCompletionString( tor->cpStatus ),
1186                            getCompletionString( cpStatus ) );
1187        }
1188
1189        tor->cpStatus = cpStatus;
1190        fireStatusChange( tor, cpStatus );
1191
1192        if( recentChange && ( cpStatus == TR_CP_COMPLETE ) )
1193            tr_trackerCompleted( tor->tracker );
1194
1195        tr_torrentSaveResume( tor );
1196    }
1197
1198    tr_torrentUnlock( tor );
1199}
1200
1201int
1202tr_torrentIsSeed( const tr_torrent * tor )
1203{
1204    return tor->cpStatus==TR_CP_COMPLETE || tor->cpStatus==TR_CP_DONE;
1205}
1206
1207/**
1208***  File priorities
1209**/
1210
1211void
1212tr_torrentInitFilePriority( tr_torrent      * tor,
1213                            tr_file_index_t   fileIndex,
1214                            tr_priority_t     priority )
1215{
1216    tr_piece_index_t i;
1217    tr_file * file;
1218
1219    assert( tor != NULL );
1220    assert( fileIndex < tor->info.fileCount );
1221    assert( priority==TR_PRI_LOW || priority==TR_PRI_NORMAL || priority==TR_PRI_HIGH );
1222
1223    file = &tor->info.files[fileIndex];
1224    file->priority = priority;
1225    for( i=file->firstPiece; i<=file->lastPiece; ++i )
1226      tor->info.pieces[i].priority = calculatePiecePriority( tor, i, fileIndex );
1227}
1228
1229void
1230tr_torrentSetFilePriorities( tr_torrent       * tor,
1231                             tr_file_index_t  * files,
1232                             tr_file_index_t    fileCount,
1233                             tr_priority_t      priority )
1234{
1235    tr_file_index_t i;
1236    tr_torrentLock( tor );
1237
1238    for( i=0; i<fileCount; ++i )
1239        tr_torrentInitFilePriority( tor, files[i], priority );
1240
1241    tr_torrentSaveResume( tor );
1242    tr_torrentUnlock( tor );
1243}
1244
1245tr_priority_t
1246tr_torrentGetFilePriority( const tr_torrent *  tor, tr_file_index_t file )
1247{
1248    tr_priority_t ret;
1249
1250    tr_torrentLock( tor );
1251    assert( tor != NULL );
1252    assert( file < tor->info.fileCount );
1253    ret = tor->info.files[file].priority;
1254    tr_torrentUnlock( tor );
1255
1256    return ret;
1257}
1258
1259tr_priority_t*
1260tr_torrentGetFilePriorities( const tr_torrent * tor )
1261{
1262    tr_file_index_t i;
1263    tr_priority_t * p;
1264
1265    tr_torrentLock( tor );
1266    p = tr_new0( tr_priority_t, tor->info.fileCount );
1267    for( i=0; i<tor->info.fileCount; ++i )
1268        p[i] = tor->info.files[i].priority;
1269    tr_torrentUnlock( tor );
1270
1271    return p;
1272}
1273
1274/**
1275***  File DND
1276**/
1277
1278int
1279tr_torrentGetFileDL( const tr_torrent * tor,
1280                     tr_file_index_t    file )
1281{
1282    int doDownload;
1283    tr_torrentLock( tor );
1284
1285    assert( file < tor->info.fileCount );
1286    doDownload = !tor->info.files[file].dnd;
1287
1288    tr_torrentUnlock( tor );
1289    return doDownload != 0;
1290}
1291
1292static void
1293setFileDND( tr_torrent      * tor,
1294            tr_file_index_t   fileIndex,
1295            int               doDownload )
1296{
1297    tr_file * file;
1298    const int dnd = !doDownload;
1299    tr_piece_index_t firstPiece, firstPieceDND;
1300    tr_piece_index_t lastPiece, lastPieceDND;
1301    tr_file_index_t i;
1302
1303    file = &tor->info.files[fileIndex];
1304    file->dnd = dnd;
1305    firstPiece = file->firstPiece;
1306    lastPiece = file->lastPiece;
1307
1308    /* can't set the first piece to DND unless
1309       every file using that piece is DND */
1310    firstPieceDND = dnd;
1311    if( fileIndex > 0 ) {
1312        for( i=fileIndex-1; firstPieceDND; --i ) {
1313            if( tor->info.files[i].lastPiece != firstPiece )
1314                break;
1315            firstPieceDND = tor->info.files[i].dnd;
1316            if( !i )
1317                break;
1318        }
1319    }
1320
1321    /* can't set the last piece to DND unless
1322       every file using that piece is DND */
1323    lastPieceDND = dnd;
1324    for( i=fileIndex+1; lastPieceDND && i<tor->info.fileCount; ++i ) {
1325        if( tor->info.files[i].firstPiece != lastPiece )
1326            break;
1327        lastPieceDND = tor->info.files[i].dnd;
1328    }
1329
1330    if( firstPiece == lastPiece )
1331    {
1332        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
1333    }
1334    else
1335    {
1336        tr_piece_index_t pp;
1337        tor->info.pieces[firstPiece].dnd = firstPieceDND;
1338        tor->info.pieces[lastPiece].dnd = lastPieceDND;
1339        for( pp=firstPiece+1; pp<lastPiece; ++pp )
1340            tor->info.pieces[pp].dnd = dnd;
1341    }
1342}
1343
1344void
1345tr_torrentInitFileDLs ( tr_torrent       * tor,
1346                        tr_file_index_t  * files,
1347                        tr_file_index_t    fileCount,
1348                        int                doDownload )
1349{
1350    tr_file_index_t i;
1351    tr_torrentLock( tor );
1352
1353    for( i=0; i<fileCount; ++i )
1354        setFileDND( tor, files[i], doDownload );
1355    tr_cpInvalidateDND ( tor->completion );
1356
1357    tr_torrentUnlock( tor );
1358}
1359
1360void
1361tr_torrentSetFileDLs ( tr_torrent      * tor,
1362                       tr_file_index_t * files,
1363                       tr_file_index_t   fileCount,
1364                       int               doDownload )
1365{
1366    tr_torrentLock( tor );
1367    tr_torrentInitFileDLs( tor, files, fileCount, doDownload );
1368    tr_torrentSaveResume( tor );
1369    tr_torrentUnlock( tor );
1370}
1371
1372/***
1373****
1374***/
1375
1376void
1377tr_torrentSetPeerLimit( tr_torrent  * tor,
1378                        uint16_t      maxConnectedPeers )
1379{
1380    tor->maxConnectedPeers = maxConnectedPeers;
1381}
1382
1383uint16_t
1384tr_torrentGetPeerLimit( const tr_torrent  * tor )
1385{
1386    return tor->maxConnectedPeers;
1387}
1388
1389/***
1390****
1391***/
1392
1393tr_block_index_t
1394_tr_block( const tr_torrent  * tor,
1395           tr_piece_index_t    index,
1396           uint32_t            offset )
1397{
1398    const tr_info * inf = &tor->info;
1399    tr_block_index_t ret;
1400    ret = index;
1401    ret *= ( inf->pieceSize / tor->blockSize );
1402    ret += offset / tor->blockSize;
1403    return ret;
1404}
1405
1406int
1407tr_torrentReqIsValid( const tr_torrent * tor,
1408                      tr_piece_index_t   index,
1409                      uint32_t           offset,
1410                      uint32_t           length )
1411{
1412    int err = 0;
1413
1414    if( index >= tor->info.pieceCount )
1415        err = 1;
1416    else if ( offset >= tr_torPieceCountBytes( tor, index ) )
1417        err = 2;
1418    else if( length > MAX_BLOCK_SIZE )
1419        err = 3;
1420    else if( tr_pieceOffset( tor, index, offset, length ) > tor->info.totalSize )
1421        err = 4;
1422
1423    return !err;
1424}
1425
1426
1427uint64_t
1428tr_pieceOffset( const tr_torrent * tor,
1429                tr_piece_index_t   index,
1430                uint32_t           offset,
1431                uint32_t           length )
1432{
1433    uint64_t ret;
1434    ret = tor->info.pieceSize;
1435    ret *= index;
1436    ret += offset;
1437    ret += length;
1438    return ret;
1439}
1440
1441/***
1442****
1443***/
1444
1445int
1446tr_torrentIsPieceChecked( const tr_torrent * tor, tr_piece_index_t piece )
1447{
1448    return tr_bitfieldHas( tor->checkedPieces, piece );
1449}
1450
1451void
1452tr_torrentSetPieceChecked( tr_torrent * tor, tr_piece_index_t piece, int isChecked )
1453{
1454    if( isChecked )
1455        tr_bitfieldAdd( tor->checkedPieces, piece );
1456    else
1457        tr_bitfieldRem( tor->checkedPieces, piece );
1458}
1459
1460void
1461tr_torrentSetFileChecked( tr_torrent * tor, tr_file_index_t fileIndex, int isChecked )
1462{
1463    const tr_file * file = &tor->info.files[fileIndex];
1464    const tr_piece_index_t begin = file->firstPiece;
1465    const tr_piece_index_t end = file->lastPiece + 1;
1466
1467    if( isChecked )
1468        tr_bitfieldAddRange ( tor->checkedPieces, begin, end );
1469    else
1470        tr_bitfieldRemRange ( tor->checkedPieces, begin, end );
1471}
1472
1473int
1474tr_torrentIsFileChecked( const tr_torrent * tor, tr_file_index_t fileIndex )
1475{
1476    const tr_file * file = &tor->info.files[fileIndex];
1477    const tr_piece_index_t begin = file->firstPiece;
1478    const tr_piece_index_t end = file->lastPiece + 1;
1479    tr_piece_index_t i;
1480    int isChecked = TRUE;
1481
1482    for( i=begin; isChecked && i<end; ++i )
1483        if( !tr_torrentIsPieceChecked( tor, i ) )
1484            isChecked = FALSE;
1485
1486    return isChecked;
1487}
1488
1489void
1490tr_torrentUncheck( tr_torrent * tor )
1491{
1492    tr_bitfieldRemRange ( tor->checkedPieces, 0, tor->info.pieceCount );
1493}
1494
1495int
1496tr_torrentCountUncheckedPieces( const tr_torrent * tor )
1497{
1498    return tor->info.pieceCount - tr_bitfieldCountTrueBits( tor->checkedPieces );
1499}
1500
1501time_t*
1502tr_torrentGetMTimes( const tr_torrent * tor, int * setme_n )
1503{
1504    int i;
1505    const int n = tor->info.fileCount;
1506    time_t * m = tr_new0( time_t, n );
1507
1508    for( i=0; i<n; ++i ) {
1509        char fname[MAX_PATH_LENGTH];
1510        struct stat sb;
1511        tr_buildPath( fname, sizeof(fname),
1512                      tor->downloadDir, tor->info.files[i].name, NULL );
1513        if ( !stat( fname, &sb ) ) {
1514#ifdef SYS_DARWIN
1515            m[i] = sb.st_mtimespec.tv_sec;
1516#else
1517            m[i] = sb.st_mtime;
1518#endif
1519        }
1520    }
1521
1522    *setme_n = n;
1523    return m;
1524}
1525
1526/***
1527****
1528***/
1529
1530void
1531tr_torrentSetAnnounceList( tr_torrent             * tor,
1532                           const tr_tracker_info  * trackers,
1533                           int                      trackerCount )
1534{
1535    tr_benc metainfo = BENC_INIT;
1536
1537    /* save to the .torrent file */
1538    if( !tr_bencLoadFile( tor->info.torrent, &metainfo ) )
1539    {
1540        int i;
1541        int prevTier = -1;
1542        tr_benc * tier = NULL;
1543        tr_benc * announceList;
1544        tr_info tmpInfo;
1545
1546        /* remove the old fields */
1547        tr_bencDictRemove( &metainfo, "announce" );
1548        tr_bencDictRemove( &metainfo, "announce-list" );
1549
1550        /* add the new fields */
1551        tr_bencDictAddStr( &metainfo, "announce", trackers[0].announce );
1552        announceList = tr_bencDictAddList( &metainfo, "announce-list", 0 );
1553        for( i=0; i<trackerCount; ++i ) {
1554            if( prevTier != trackers[i].tier ) {
1555                prevTier = trackers[i].tier;
1556                tier = tr_bencListAddList( announceList, 0 );
1557            }
1558            tr_bencListAddStr( tier, trackers[i].announce );
1559        }
1560
1561        /* try to parse it back again, to make sure it's good */
1562        memset( &tmpInfo, 0, sizeof( tr_info ) );
1563        if( !tr_metainfoParse( tor->handle, &tmpInfo, &metainfo ) )
1564        {
1565            /* if it's good, save it and use it */
1566            tr_metainfoFree( &tor->info );
1567            tor->info = tmpInfo;
1568            tr_torrentInitFilePieces( tor );
1569            tr_bencSaveFile( tor->info.torrent, &metainfo );
1570        }
1571
1572        /* cleanup */ 
1573        tr_bencFree( &metainfo );
1574    }
1575}
Note: See TracBrowser for help on using the repository browser.