source: trunk/libtransmission/torrent.c @ 7154

Last change on this file since 7154 was 7154, checked in by charles, 12 years ago

(libT) yet another stab at getting bandwidth management under control. this version may suck less than previous attempts. It also breaks the mac build until someone adds iobuf.[ch] to xcode...

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