source: trunk/libtransmission/torrent.c @ 7055

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

update NEWS

  • Property svn:keywords set to Date Rev Author Id
File size: 44.4 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 7055 2008-11-06 02:56:51Z 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( t->pieceSpeed[TR_PEER_TO_CLIENT] );
1082    tr_rcClose( t->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.