source: trunk/libtransmission/torrent.c @ 7056

Last change on this file since 7056 was 7056, checked in by charles, 13 years ago

(libT) fix r7055 oops. Also, the comment on that last commit should read "in the values returned by the public API's speed functions, only give the piece data transfer speeds -- don't include the protocol overheads"

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