source: trunk/libtransmission/torrent.c @ 7329

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

(trunk libT) if an individual torrent's speed limit is set, don't pay attention to the global speed limit

  • Property svn:keywords set to Date Rev Author Id
File size: 45.1 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 7329 2008-12-09 15:09:26Z 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_GLOBAL );
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
488    tor->blockSize = getBlockSize( info->pieceSize );
489
490    tor->lastPieceSize = info->totalSize % info->pieceSize;
491
492    if( !tor->lastPieceSize )
493        tor->lastPieceSize = info->pieceSize;
494
495    tor->lastBlockSize = info->totalSize % tor->blockSize;
496
497    if( !tor->lastBlockSize )
498        tor->lastBlockSize = tor->blockSize;
499
500    tor->blockCount =
501        ( info->totalSize + tor->blockSize - 1 ) / tor->blockSize;
502
503    tor->blockCountInPiece =
504        info->pieceSize / tor->blockSize;
505
506    tor->blockCountInLastPiece =
507        ( tor->lastPieceSize + tor->blockSize - 1 ) / tor->blockSize;
508
509    /* check our work */
510    assert( ( info->pieceSize % tor->blockSize ) == 0 );
511    t = info->pieceCount - 1;
512    t *= info->pieceSize;
513    t += tor->lastPieceSize;
514    assert( t == info->totalSize );
515    t = tor->blockCount - 1;
516    t *= tor->blockSize;
517    t += tor->lastBlockSize;
518    assert( t == info->totalSize );
519    t = info->pieceCount - 1;
520    t *= tor->blockCountInPiece;
521    t += tor->blockCountInLastPiece;
522    assert( t == (uint64_t)tor->blockCount );
523
524    tor->completion = tr_cpInit( tor );
525
526    tr_torrentInitFilePieces( tor );
527
528    tor->swarmSpeed = tr_rcInit( );
529
530    tr_sha1( tor->obfuscatedHash, "req2", 4,
531             info->hash, SHA_DIGEST_LENGTH,
532             NULL );
533
534    tr_peerMgrAddTorrent( h->peerMgr, tor );
535
536    if( !h->isPortSet )
537        tr_sessionSetPeerPort( h, TR_DEFAULT_PORT );
538
539    assert( !tor->downloadedCur );
540    assert( !tor->uploadedCur );
541
542    tor->error   = 0;
543
544    tor->checkedPieces = tr_bitfieldNew( tor->info.pieceCount );
545    tr_torrentUncheck( tor );
546
547    tor->addedDate = time( NULL ); /* this is a default value to be
548                                      overwritten by the resume file */
549    loaded = tr_torrentLoadResume( tor, ~0, ctor );
550
551    doStart = tor->isRunning;
552    tor->isRunning = 0;
553
554    if( !( loaded & TR_FR_SPEEDLIMIT ) )
555    {
556        tr_torrentSetSpeedLimit( tor, TR_UP,
557                                tr_sessionGetSpeedLimit( tor->session, TR_UP ) );
558        tr_torrentSetSpeedLimit( tor, TR_DOWN,
559                                tr_sessionGetSpeedLimit( tor->session,
560                                                         TR_DOWN ) );
561    }
562
563    tor->completeness = tr_cpGetStatus( tor->completion );
564
565    tor->tracker = tr_trackerNew( tor );
566    tor->trackerSubscription =
567        tr_trackerSubscribe( tor->tracker, onTrackerResponse,
568                             tor );
569
570    {
571        tr_torrent * it = NULL;
572        tr_torrent * last = NULL;
573        while( ( it = tr_torrentNext( h, it ) ) )
574            last = it;
575
576        if( !last )
577            h->torrentList = tor;
578        else
579            last->next = tor;
580        ++h->torrentCount;
581    }
582
583    tr_globalUnlock( h );
584
585    /* maybe save our own copy of the metainfo */
586    if( tr_ctorGetSave( ctor ) )
587    {
588        const tr_benc * val;
589        if( !tr_ctorGetMetainfo( ctor, &val ) )
590        {
591            const char * filename = tor->info.torrent;
592            tr_bencSaveFile( filename, val );
593            tr_sessionSetTorrentFile( tor->session, tor->info.hashString,
594                                      filename );
595        }
596    }
597
598    tr_metainfoMigrate( h, &tor->info );
599
600    if( doStart )
601        torrentStart( tor, FALSE );
602}
603
604int
605tr_torrentParse( const tr_handle * handle,
606                 const tr_ctor *   ctor,
607                 tr_info *         setmeInfo )
608{
609    int             err = 0;
610    int             doFree;
611    tr_info         tmp;
612    const tr_benc * metainfo;
613
614    if( setmeInfo == NULL )
615        setmeInfo = &tmp;
616    memset( setmeInfo, 0, sizeof( tr_info ) );
617
618    if( !err && tr_ctorGetMetainfo( ctor, &metainfo ) )
619        return TR_EINVALID;
620
621    err = tr_metainfoParse( handle, setmeInfo, metainfo );
622    doFree = !err && ( setmeInfo == &tmp );
623
624    if( !err && !getBlockSize( setmeInfo->pieceSize ) )
625        err = TR_EINVALID;
626
627    if( !err && tr_torrentExists( handle, setmeInfo->hash ) )
628        err = TR_EDUPLICATE;
629
630    if( doFree )
631        tr_metainfoFree( setmeInfo );
632
633    return err;
634}
635
636tr_torrent *
637tr_torrentNew( tr_handle *     handle,
638               const tr_ctor * ctor,
639               int *           setmeError )
640{
641    int          err;
642    tr_info      tmpInfo;
643    tr_torrent * tor = NULL;
644
645    err = tr_torrentParse( handle, ctor, &tmpInfo );
646    if( !err )
647    {
648        tor = tr_new0( tr_torrent, 1 );
649        tor->info = tmpInfo;
650        torrentRealInit( handle, tor, ctor );
651    }
652    else if( setmeError )
653    {
654        *setmeError = err;
655    }
656
657    return tor;
658}
659
660/**
661***
662**/
663
664void
665tr_torrentSetDownloadDir( tr_torrent * tor,
666                          const char * path )
667{
668    if( !path || !tor->downloadDir || strcmp( path, tor->downloadDir ) )
669    {
670        tr_free( tor->downloadDir );
671        tor->downloadDir = tr_strdup( path );
672        tr_torrentSaveResume( tor );
673    }
674}
675
676const char*
677tr_torrentGetDownloadDir( const tr_torrent * tor )
678{
679    return tor->downloadDir;
680}
681
682void
683tr_torrentChangeMyPort( tr_torrent * tor )
684{
685    if( tor->tracker )
686        tr_trackerChangeMyPort( tor->tracker );
687}
688
689int
690tr_torrentIsPrivate( const tr_torrent * tor )
691{
692    return tor
693           && tor->info.isPrivate;
694}
695
696int
697tr_torrentAllowsPex( const tr_torrent * tor )
698{
699    return tor
700           && tor->session->isPexEnabled
701           && !tr_torrentIsPrivate( tor );
702}
703
704static void
705tr_torrentManualUpdateImpl( void * vtor )
706{
707    tr_torrent * tor = vtor;
708
709    if( tor->isRunning )
710        tr_trackerReannounce( tor->tracker );
711}
712
713void
714tr_torrentManualUpdate( tr_torrent * tor )
715{
716    tr_runInEventThread( tor->session, tr_torrentManualUpdateImpl, tor );
717}
718
719int
720tr_torrentCanManualUpdate( const tr_torrent * tor )
721{
722    return ( tor )
723           && ( tor->isRunning )
724           && ( tr_trackerCanManualAnnounce( tor->tracker ) );
725}
726
727const tr_info *
728tr_torrentInfo( const tr_torrent * tor )
729{
730    return tor ? &tor->info : NULL;
731}
732
733const tr_stat *
734tr_torrentStatCached( tr_torrent * tor )
735{
736    const time_t now = time( NULL );
737
738    return tor && ( now == tor->lastStatTime ) ? &tor->stats
739           : tr_torrentStat( tor );
740}
741
742tr_torrent_activity
743tr_torrentGetActivity( tr_torrent * tor )
744{
745    tr_torrentRecheckCompleteness( tor );
746
747    if( tor->verifyState == TR_VERIFY_NOW )
748        return TR_STATUS_CHECK;
749    if( tor->verifyState == TR_VERIFY_WAIT )
750        return TR_STATUS_CHECK_WAIT;
751    if( !tor->isRunning )
752        return TR_STATUS_STOPPED;
753    if( tor->completeness == TR_LEECH )
754        return TR_STATUS_DOWNLOAD;
755
756    return TR_STATUS_SEED;
757}
758
759const tr_stat *
760tr_torrentStat( tr_torrent * tor )
761{
762    tr_stat *               s;
763    struct tr_tracker *     tc;
764    const tr_tracker_info * ti;
765    int                     usableSeeds = 0;
766
767    if( !tor )
768        return NULL;
769
770    tr_torrentLock( tor );
771
772    tor->lastStatTime = time( NULL );
773
774    s = &tor->stats;
775    s->id = tor->uniqueId;
776    s->activity = tr_torrentGetActivity( tor );
777    s->error  = tor->error;
778    memcpy( s->errorString, tor->errorString,
779           sizeof( s->errorString ) );
780
781    tc = tor->tracker;
782    ti = tr_trackerGetAddress( tor->tracker );
783    s->announceURL = ti ? ti->announce : NULL;
784    s->scrapeURL   = ti ? ti->scrape   : NULL;
785    tr_trackerStat( tc, s );
786
787    tr_trackerGetCounts( tc, &s->timesCompleted,
788                             &s->leechers,
789                             &s->seeders,
790                             &s->downloaders );
791
792    tr_peerMgrTorrentStats( tor->session->peerMgr,
793                            tor->info.hash,
794                            &s->peersKnown,
795                            &s->peersConnected,
796                            &usableSeeds,
797                            &s->webseedsSendingToUs,
798                            &s->peersSendingToUs,
799                            &s->peersGettingFromUs,
800                            s->peersFrom );
801
802    s->rawUploadSpeed     = tr_bandwidthGetRawSpeed  ( tor->bandwidth, TR_UP );
803    s->rawDownloadSpeed   = tr_bandwidthGetRawSpeed  ( tor->bandwidth, TR_DOWN );
804    s->pieceUploadSpeed   = tr_bandwidthGetPieceSpeed( tor->bandwidth, TR_UP );
805    s->pieceDownloadSpeed = tr_bandwidthGetPieceSpeed( tor->bandwidth, TR_DOWN );
806
807    usableSeeds += tor->info.webseedCount;
808
809    s->percentComplete = tr_cpPercentComplete ( tor->completion );
810
811    s->percentDone = tr_cpPercentDone( tor->completion );
812    s->leftUntilDone = tr_cpLeftUntilDone( tor->completion );
813    s->sizeWhenDone = tr_cpSizeWhenDone( tor->completion );
814
815    s->recheckProgress = s->activity == TR_STATUS_CHECK
816                       ? 1.0 -
817                         ( tr_torrentCountUncheckedPieces( tor ) /
818                           (double) tor->info.pieceCount )
819                       : 0.0;
820
821    s->swarmSpeed = tr_rcRate( tor->swarmSpeed );
822
823    s->activityDate = tor->activityDate;
824    s->addedDate    = tor->addedDate;
825    s->doneDate     = tor->doneDate;
826    s->startDate    = tor->startDate;
827
828    s->corruptEver     = tor->corruptCur    + tor->corruptPrev;
829    s->downloadedEver  = tor->downloadedCur + tor->downloadedPrev;
830    s->uploadedEver    = tor->uploadedCur   + tor->uploadedPrev;
831    s->haveValid       = tr_cpHaveValid( tor->completion );
832    s->haveUnchecked   = tr_cpHaveTotal( tor->completion ) - s->haveValid;
833
834
835    if( usableSeeds > 0 )
836    {
837        s->desiredAvailable = s->leftUntilDone;
838    }
839    else if( !s->leftUntilDone || !s->peersConnected )
840    {
841        s->desiredAvailable = 0;
842    }
843    else
844    {
845        tr_piece_index_t i;
846        tr_bitfield *    peerPieces = tr_peerMgrGetAvailable(
847            tor->session->peerMgr,
848            tor->info.
849            hash );
850        s->desiredAvailable = 0;
851        for( i = 0; i < tor->info.pieceCount; ++i )
852            if( !tor->info.pieces[i].dnd && tr_bitfieldHas( peerPieces, i ) )
853                s->desiredAvailable += tr_cpMissingBlocksInPiece(
854                    tor->completion, i );
855        s->desiredAvailable *= tor->blockSize;
856        tr_bitfieldFree( peerPieces );
857    }
858
859    if( s->leftUntilDone > s->desiredAvailable )
860        s->eta = TR_ETA_NOT_AVAIL;
861    else if( s->pieceDownloadSpeed < 0.1 )
862        s->eta = TR_ETA_UNKNOWN;
863    else
864        s->eta = s->leftUntilDone / s->pieceDownloadSpeed / 1024.0;
865
866    s->ratio = tr_getRatio(
867        s->uploadedEver,
868        s->downloadedEver ? s->downloadedEver : s->
869        haveValid );
870
871    tr_torrentUnlock( tor );
872
873    return s;
874}
875
876/***
877****
878***/
879
880static uint64_t
881fileBytesCompleted( const tr_torrent * tor,
882                    tr_file_index_t    fileIndex )
883{
884    const tr_file *        file     =  &tor->info.files[fileIndex];
885    const tr_block_index_t firstBlock       =  file->offset /
886                                              tor->blockSize;
887    const uint64_t         firstBlockOffset =  file->offset %
888                                              tor->blockSize;
889    const uint64_t         lastOffset       =
890        file->length ? ( file->length - 1 ) : 0;
891    const tr_block_index_t lastBlock        =
892        ( file->offset + lastOffset ) / tor->blockSize;
893    const uint64_t         lastBlockOffset  =
894        ( file->offset + lastOffset ) % tor->blockSize;
895    uint64_t               haveBytes = 0;
896
897    assert( tor );
898    assert( fileIndex < tor->info.fileCount );
899    assert( file->offset + file->length <= tor->info.totalSize );
900    assert( ( firstBlock < tor->blockCount )
901          || ( !file->length && file->offset == tor->info.totalSize ) );
902    assert( ( lastBlock < tor->blockCount )
903          || ( !file->length && file->offset == tor->info.totalSize ) );
904    assert( firstBlock <= lastBlock );
905    assert( tr_torBlockPiece( tor, firstBlock ) == file->firstPiece );
906    assert( tr_torBlockPiece( tor, lastBlock ) == file->lastPiece );
907
908    if( firstBlock == lastBlock )
909    {
910        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
911            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
912    }
913    else
914    {
915        tr_block_index_t i;
916
917        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
918            haveBytes += tor->blockSize - firstBlockOffset;
919
920        for( i = firstBlock + 1; i < lastBlock; ++i )
921            if( tr_cpBlockIsComplete( tor->completion, i ) )
922                haveBytes += tor->blockSize;
923
924        if( tr_cpBlockIsComplete( tor->completion, lastBlock ) )
925            haveBytes += lastBlockOffset + 1;
926    }
927
928    return haveBytes;
929}
930
931tr_file_stat *
932tr_torrentFiles( const tr_torrent * tor,
933                 tr_file_index_t *  fileCount )
934{
935    tr_file_index_t       i;
936    const tr_file_index_t n = tor->info.fileCount;
937    tr_file_stat *        files = tr_new0( tr_file_stat, n );
938    tr_file_stat *        walk = files;
939
940    for( i = 0; i < n; ++i, ++walk )
941    {
942        const uint64_t b = fileBytesCompleted( tor, i );
943        walk->bytesCompleted = b;
944        walk->progress = tr_getRatio( b, tor->info.files[i].length );
945    }
946
947    if( fileCount )
948        *fileCount = n;
949
950    return files;
951}
952
953void
954tr_torrentFilesFree( tr_file_stat *            files,
955                     tr_file_index_t fileCount UNUSED )
956{
957    tr_free( files );
958}
959
960/***
961****
962***/
963
964float*
965tr_torrentWebSpeeds( const tr_torrent * tor )
966{
967    return tor ? tr_peerMgrWebSpeeds( tor->session->peerMgr, tor->info.hash )
968           : NULL;
969}
970
971tr_peer_stat *
972tr_torrentPeers( const tr_torrent * tor,
973                 int *              peerCount )
974{
975    tr_peer_stat * ret = NULL;
976
977    if( tor )
978        ret = tr_peerMgrPeerStats( tor->session->peerMgr,
979                                   tor->info.hash, peerCount );
980
981    return ret;
982}
983
984void
985tr_torrentPeersFree( tr_peer_stat * peers,
986                     int peerCount  UNUSED )
987{
988    tr_free( peers );
989}
990
991void
992tr_torrentAvailability( const tr_torrent * tor,
993                        int8_t *           tab,
994                        int                size )
995{
996    tr_peerMgrTorrentAvailability( tor->session->peerMgr,
997                                   tor->info.hash,
998                                   tab, size );
999}
1000
1001void
1002tr_torrentAmountFinished( const tr_torrent * tor,
1003                          float *            tab,
1004                          int                size )
1005{
1006    tr_torrentLock( tor );
1007    tr_cpGetAmountDone( tor->completion, tab, size );
1008    tr_torrentUnlock( tor );
1009}
1010
1011void
1012tr_torrentResetTransferStats( tr_torrent * tor )
1013{
1014    tr_torrentLock( tor );
1015
1016    tor->downloadedPrev += tor->downloadedCur;
1017    tor->downloadedCur   = 0;
1018    tor->uploadedPrev   += tor->uploadedCur;
1019    tor->uploadedCur     = 0;
1020    tor->corruptPrev    += tor->corruptCur;
1021    tor->corruptCur      = 0;
1022
1023    tr_torrentUnlock( tor );
1024}
1025
1026void
1027tr_torrentSetHasPiece( tr_torrent *     tor,
1028                       tr_piece_index_t pieceIndex,
1029                       int              has )
1030{
1031    tr_torrentLock( tor );
1032
1033    assert( tor );
1034    assert( pieceIndex < tor->info.pieceCount );
1035
1036    if( has )
1037        tr_cpPieceAdd( tor->completion, pieceIndex );
1038    else
1039        tr_cpPieceRem( tor->completion, pieceIndex );
1040
1041    tr_torrentUnlock( tor );
1042}
1043
1044/***
1045****
1046***/
1047
1048static void
1049freeTorrent( tr_torrent * tor )
1050{
1051    tr_torrent * t;
1052    tr_handle *  h = tor->session;
1053    tr_info *    inf = &tor->info;
1054
1055    assert( tor );
1056    assert( !tor->isRunning );
1057
1058    tr_globalLock( h );
1059
1060    tr_peerMgrRemoveTorrent( h->peerMgr, tor->info.hash );
1061
1062    tr_cpClose( tor->completion );
1063
1064    tr_rcClose( tor->swarmSpeed );
1065
1066    tr_trackerUnsubscribe( tor->tracker, tor->trackerSubscription );
1067    tr_trackerFree( tor->tracker );
1068    tor->tracker = NULL;
1069
1070    tr_bitfieldFree( tor->checkedPieces );
1071
1072    tr_free( tor->downloadDir );
1073    tr_free( tor->peer_id );
1074
1075    if( tor == h->torrentList )
1076        h->torrentList = tor->next;
1077    else for( t = h->torrentList; t != NULL; t = t->next )
1078        {
1079            if( t->next == tor )
1080            {
1081                t->next = tor->next;
1082                break;
1083            }
1084        }
1085
1086    assert( h->torrentCount >= 1 );
1087    h->torrentCount--;
1088
1089    tr_bandwidthFree( tor->bandwidth );
1090
1091    tr_metainfoFree( inf );
1092    tr_free( tor );
1093
1094    tr_globalUnlock( h );
1095}
1096
1097/**
1098***  Start/Stop Callback
1099**/
1100
1101static void
1102checkAndStartImpl( void * vtor )
1103{
1104    tr_torrent * tor = vtor;
1105
1106    tr_globalLock( tor->session );
1107
1108    tor->isRunning = 1;
1109    *tor->errorString = '\0';
1110    tr_torrentResetTransferStats( tor );
1111    tor->completeness = tr_cpGetStatus( tor->completion );
1112    tr_torrentSaveResume( tor );
1113    tor->startDate = time( NULL );
1114    tr_trackerStart( tor->tracker );
1115    tr_peerMgrStartTorrent( tor->session->peerMgr, tor->info.hash );
1116
1117    tr_globalUnlock( tor->session );
1118}
1119
1120static void
1121checkAndStartCB( tr_torrent * tor )
1122{
1123    tr_runInEventThread( tor->session, checkAndStartImpl, tor );
1124}
1125
1126static void
1127torrentStart( tr_torrent * tor,
1128              int          reloadProgress )
1129{
1130    tr_globalLock( tor->session );
1131
1132    if( !tor->isRunning )
1133    {
1134        const int isVerifying = tr_verifyInProgress( tor );
1135
1136        if( !isVerifying && reloadProgress )
1137            tr_torrentLoadResume( tor, TR_FR_PROGRESS, NULL );
1138
1139        tor->isRunning = 1;
1140
1141        if( !isVerifying )
1142            tr_verifyAdd( tor, checkAndStartCB );
1143    }
1144
1145    tr_globalUnlock( tor->session );
1146}
1147
1148void
1149tr_torrentStart( tr_torrent * tor )
1150{
1151    if( tor )
1152        torrentStart( tor, TRUE );
1153}
1154
1155static void
1156torrentRecheckDoneImpl( void * vtor )
1157{
1158    tr_torrentRecheckCompleteness( vtor );
1159}
1160
1161static void
1162torrentRecheckDoneCB( tr_torrent * tor )
1163{
1164    tr_runInEventThread( tor->session, torrentRecheckDoneImpl, tor );
1165}
1166
1167void
1168tr_torrentVerify( tr_torrent * tor )
1169{
1170    tr_verifyRemove( tor );
1171
1172    tr_globalLock( tor->session );
1173
1174    tr_torrentUncheck( tor );
1175    tr_verifyAdd( tor, torrentRecheckDoneCB );
1176
1177    tr_globalUnlock( tor->session );
1178}
1179
1180static void
1181stopTorrent( void * vtor )
1182{
1183    tr_file_index_t i;
1184
1185    tr_torrent *    tor = vtor;
1186
1187    tr_verifyRemove( tor );
1188    tr_peerMgrStopTorrent( tor->session->peerMgr, tor->info.hash );
1189    tr_trackerStop( tor->tracker );
1190
1191    for( i = 0; i < tor->info.fileCount; ++i )
1192    {
1193        const tr_file * file = &tor->info.files[i];
1194        char * path = tr_buildPath( tor->downloadDir, file->name, NULL );
1195        tr_fdFileClose( path );
1196        tr_free( path );
1197    }
1198}
1199
1200void
1201tr_torrentStop( tr_torrent * tor )
1202{
1203    if( tor )
1204    {
1205        tr_globalLock( tor->session );
1206
1207        tor->isRunning = 0;
1208        if( !tor->isDeleting )
1209            tr_torrentSaveResume( tor );
1210        tr_runInEventThread( tor->session, stopTorrent, tor );
1211
1212        tr_globalUnlock( tor->session );
1213    }
1214}
1215
1216static void
1217closeTorrent( void * vtor )
1218{
1219    tr_torrent * tor = vtor;
1220
1221    tr_torrentSaveResume( tor );
1222    tor->isRunning = 0;
1223    stopTorrent( tor );
1224    if( tor->isDeleting )
1225    {
1226        tr_metainfoRemoveSaved( tor->session, &tor->info );
1227        tr_torrentRemoveResume( tor );
1228    }
1229    freeTorrent( tor );
1230}
1231
1232void
1233tr_torrentFree( tr_torrent * tor )
1234{
1235    if( tor )
1236    {
1237        tr_handle * handle = tor->session;
1238        tr_globalLock( handle );
1239
1240        tr_torrentClearCompletenessCallback( tor );
1241        tr_runInEventThread( handle, closeTorrent, tor );
1242
1243        tr_globalUnlock( handle );
1244    }
1245}
1246
1247void
1248tr_torrentRemove( tr_torrent * tor )
1249{
1250    tor->isDeleting = 1;
1251    tr_torrentFree( tor );
1252}
1253
1254/**
1255***  Completeness
1256**/
1257
1258static const char *
1259getCompletionString( int type )
1260{
1261    switch( type )
1262    {
1263        /* Translators: this is a minor point that's safe to skip over, but FYI:
1264           "Complete" and "Done" are specific, different terms in Transmission:
1265           "Complete" means we've downloaded every file in the torrent.
1266           "Done" means we're done downloading the files we wanted, but NOT all
1267           that exist */
1268        case TR_PARTIAL_SEED:
1269            return _( "Done" );
1270
1271        case TR_SEED:
1272            return _( "Complete" );
1273
1274        default:
1275            return _( "Incomplete" );
1276    }
1277}
1278
1279static void
1280fireCompletenessChange( tr_torrent       * tor,
1281                        tr_completeness    status )
1282{
1283    assert( tor );
1284    assert( ( status == TR_LEECH )
1285         || ( status == TR_SEED )
1286         || ( status == TR_PARTIAL_SEED ) );
1287
1288    if( tor->completeness_func )
1289        tor->completeness_func( tor, status, tor->completeness_func_user_data );
1290}
1291
1292void
1293tr_torrentSetCompletenessCallback( tr_torrent                    * tor,
1294                                   tr_torrent_completeness_func    func,
1295                                   void                          * user_data )
1296{
1297    assert( tor );
1298    tor->completeness_func = func;
1299    tor->completeness_func_user_data = user_data;
1300}
1301
1302void
1303tr_torrentClearCompletenessCallback( tr_torrent * torrent )
1304{
1305    tr_torrentSetCompletenessCallback( torrent, NULL, NULL );
1306}
1307
1308void
1309tr_torrentRecheckCompleteness( tr_torrent * tor )
1310{
1311    tr_completeness completeness;
1312
1313    tr_torrentLock( tor );
1314
1315    completeness = tr_cpGetStatus( tor->completion );
1316
1317    if( completeness != tor->completeness )
1318    {
1319        const int recentChange = tor->downloadedCur != 0;
1320
1321        if( recentChange )
1322        {
1323            tr_torinf( tor, _( "State changed from \"%1$s\" to \"%2$s\"" ),
1324                      getCompletionString( tor->completeness ),
1325                      getCompletionString( completeness ) );
1326        }
1327
1328        tor->completeness = completeness;
1329        fireCompletenessChange( tor, completeness );
1330
1331        if( recentChange && ( completeness == TR_SEED ) )
1332        {
1333            tr_trackerCompleted( tor->tracker );
1334
1335            tor->doneDate = time( NULL );
1336        }
1337
1338        tr_torrentSaveResume( tor );
1339    }
1340
1341    tr_torrentUnlock( tor );
1342}
1343
1344int
1345tr_torrentIsSeed( const tr_torrent * tor )
1346{
1347    return tor->completeness != TR_LEECH;
1348}
1349
1350/**
1351***  File priorities
1352**/
1353
1354void
1355tr_torrentInitFilePriority( tr_torrent *    tor,
1356                            tr_file_index_t fileIndex,
1357                            tr_priority_t   priority )
1358{
1359    tr_piece_index_t i;
1360    tr_file *        file;
1361
1362    assert( tor );
1363    assert( fileIndex < tor->info.fileCount );
1364    assert(
1365        priority == TR_PRI_LOW || priority == TR_PRI_NORMAL || priority ==
1366        TR_PRI_HIGH );
1367
1368    file = &tor->info.files[fileIndex];
1369    file->priority = priority;
1370    for( i = file->firstPiece; i <= file->lastPiece; ++i )
1371        tor->info.pieces[i].priority = calculatePiecePriority( tor, i,
1372                                                               fileIndex );
1373}
1374
1375void
1376tr_torrentSetFilePriorities( tr_torrent *      tor,
1377                             tr_file_index_t * files,
1378                             tr_file_index_t   fileCount,
1379                             tr_priority_t     priority )
1380{
1381    tr_file_index_t i;
1382
1383    tr_torrentLock( tor );
1384
1385    for( i = 0; i < fileCount; ++i )
1386        tr_torrentInitFilePriority( tor, files[i], priority );
1387
1388    tr_torrentSaveResume( tor );
1389    tr_torrentUnlock( tor );
1390}
1391
1392tr_priority_t
1393tr_torrentGetFilePriority( const tr_torrent * tor,
1394                           tr_file_index_t    file )
1395{
1396    tr_priority_t ret;
1397
1398    tr_torrentLock( tor );
1399    assert( tor );
1400    assert( file < tor->info.fileCount );
1401    ret = tor->info.files[file].priority;
1402    tr_torrentUnlock( tor );
1403
1404    return ret;
1405}
1406
1407tr_priority_t*
1408tr_torrentGetFilePriorities( const tr_torrent * tor )
1409{
1410    tr_file_index_t i;
1411    tr_priority_t * p;
1412
1413    tr_torrentLock( tor );
1414    p = tr_new0( tr_priority_t, tor->info.fileCount );
1415    for( i = 0; i < tor->info.fileCount; ++i )
1416        p[i] = tor->info.files[i].priority;
1417    tr_torrentUnlock( tor );
1418
1419    return p;
1420}
1421
1422/**
1423***  File DND
1424**/
1425
1426int
1427tr_torrentGetFileDL( const tr_torrent * tor,
1428                     tr_file_index_t    file )
1429{
1430    int doDownload;
1431
1432    tr_torrentLock( tor );
1433
1434    assert( file < tor->info.fileCount );
1435    doDownload = !tor->info.files[file].dnd;
1436
1437    tr_torrentUnlock( tor );
1438    return doDownload != 0;
1439}
1440
1441static void
1442setFileDND( tr_torrent *    tor,
1443            tr_file_index_t fileIndex,
1444            int             doDownload )
1445{
1446    tr_file *        file;
1447    const int        dnd = !doDownload;
1448    tr_piece_index_t firstPiece, firstPieceDND;
1449    tr_piece_index_t lastPiece, lastPieceDND;
1450    tr_file_index_t  i;
1451
1452    file = &tor->info.files[fileIndex];
1453    file->dnd = dnd;
1454    firstPiece = file->firstPiece;
1455    lastPiece = file->lastPiece;
1456
1457    /* can't set the first piece to DND unless
1458       every file using that piece is DND */
1459    firstPieceDND = dnd;
1460    if( fileIndex > 0 )
1461    {
1462        for( i = fileIndex - 1; firstPieceDND; --i )
1463        {
1464            if( tor->info.files[i].lastPiece != firstPiece )
1465                break;
1466            firstPieceDND = tor->info.files[i].dnd;
1467            if( !i )
1468                break;
1469        }
1470    }
1471
1472    /* can't set the last piece to DND unless
1473       every file using that piece is DND */
1474    lastPieceDND = dnd;
1475    for( i = fileIndex + 1; lastPieceDND && i < tor->info.fileCount; ++i )
1476    {
1477        if( tor->info.files[i].firstPiece != lastPiece )
1478            break;
1479        lastPieceDND = tor->info.files[i].dnd;
1480    }
1481
1482    if( firstPiece == lastPiece )
1483    {
1484        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
1485    }
1486    else
1487    {
1488        tr_piece_index_t pp;
1489        tor->info.pieces[firstPiece].dnd = firstPieceDND;
1490        tor->info.pieces[lastPiece].dnd = lastPieceDND;
1491        for( pp = firstPiece + 1; pp < lastPiece; ++pp )
1492            tor->info.pieces[pp].dnd = dnd;
1493    }
1494}
1495
1496void
1497tr_torrentInitFileDLs( tr_torrent *      tor,
1498                       tr_file_index_t * files,
1499                       tr_file_index_t   fileCount,
1500                       int               doDownload )
1501{
1502    tr_file_index_t i;
1503
1504    tr_torrentLock( tor );
1505
1506    for( i = 0; i < fileCount; ++i )
1507        setFileDND( tor, files[i], doDownload );
1508    tr_cpInvalidateDND ( tor->completion );
1509
1510    tr_torrentUnlock( tor );
1511}
1512
1513void
1514tr_torrentSetFileDLs( tr_torrent *      tor,
1515                      tr_file_index_t * files,
1516                      tr_file_index_t   fileCount,
1517                      int               doDownload )
1518{
1519    tr_torrentLock( tor );
1520    tr_torrentInitFileDLs( tor, files, fileCount, doDownload );
1521    tr_torrentSaveResume( tor );
1522    tr_torrentUnlock( tor );
1523}
1524
1525/***
1526****
1527***/
1528
1529void
1530tr_torrentSetPeerLimit( tr_torrent * tor,
1531                        uint16_t     maxConnectedPeers )
1532{
1533    tor->maxConnectedPeers = maxConnectedPeers;
1534}
1535
1536uint16_t
1537tr_torrentGetPeerLimit( const tr_torrent * tor )
1538{
1539    return tor->maxConnectedPeers;
1540}
1541
1542/***
1543****
1544***/
1545
1546tr_block_index_t
1547_tr_block( const tr_torrent * tor,
1548           tr_piece_index_t   index,
1549           uint32_t           offset )
1550{
1551    const tr_info *  inf = &tor->info;
1552    tr_block_index_t ret;
1553
1554    ret = index;
1555    ret *= ( inf->pieceSize / tor->blockSize );
1556    ret += offset / tor->blockSize;
1557    return ret;
1558}
1559
1560int
1561tr_torrentReqIsValid( const tr_torrent * tor,
1562                      tr_piece_index_t   index,
1563                      uint32_t           offset,
1564                      uint32_t           length )
1565{
1566    int err = 0;
1567
1568    if( index >= tor->info.pieceCount )
1569        err = 1;
1570    else if( length < 1 )
1571        err = 2;
1572    else if( ( offset + length ) > tr_torPieceCountBytes( tor, index ) )
1573        err = 3;
1574    else if( length > MAX_BLOCK_SIZE )
1575        err = 4;
1576    else if( tr_pieceOffset( tor, index, offset, length ) > tor->info.totalSize )
1577        err = 5;
1578
1579    if( err ) fprintf( stderr, "index %lu offset %lu length %lu err %d\n",
1580                       (unsigned long)index, (unsigned long)offset,
1581                       (unsigned long)length,
1582                       err );
1583
1584    return !err;
1585}
1586
1587uint64_t
1588tr_pieceOffset( const tr_torrent * tor,
1589                tr_piece_index_t   index,
1590                uint32_t           offset,
1591                uint32_t           length )
1592{
1593    uint64_t ret;
1594
1595    ret = tor->info.pieceSize;
1596    ret *= index;
1597    ret += offset;
1598    ret += length;
1599    return ret;
1600}
1601
1602/***
1603****
1604***/
1605
1606int
1607tr_torrentIsPieceChecked( const tr_torrent * tor,
1608                          tr_piece_index_t   piece )
1609{
1610    return tr_bitfieldHas( tor->checkedPieces, piece );
1611}
1612
1613void
1614tr_torrentSetPieceChecked( tr_torrent *     tor,
1615                           tr_piece_index_t piece,
1616                           int              isChecked )
1617{
1618    if( isChecked )
1619        tr_bitfieldAdd( tor->checkedPieces, piece );
1620    else
1621        tr_bitfieldRem( tor->checkedPieces, piece );
1622}
1623
1624void
1625tr_torrentSetFileChecked( tr_torrent *    tor,
1626                          tr_file_index_t fileIndex,
1627                          int             isChecked )
1628{
1629    const tr_file *        file = &tor->info.files[fileIndex];
1630    const tr_piece_index_t begin = file->firstPiece;
1631    const tr_piece_index_t end = file->lastPiece + 1;
1632
1633    if( isChecked )
1634        tr_bitfieldAddRange ( tor->checkedPieces, begin, end );
1635    else
1636        tr_bitfieldRemRange ( tor->checkedPieces, begin, end );
1637}
1638
1639int
1640tr_torrentIsFileChecked( const tr_torrent * tor,
1641                         tr_file_index_t    fileIndex )
1642{
1643    const tr_file *        file = &tor->info.files[fileIndex];
1644    const tr_piece_index_t begin = file->firstPiece;
1645    const tr_piece_index_t end = file->lastPiece + 1;
1646    tr_piece_index_t       i;
1647    int                    isChecked = TRUE;
1648
1649    for( i = begin; isChecked && i < end; ++i )
1650        if( !tr_torrentIsPieceChecked( tor, i ) )
1651            isChecked = FALSE;
1652
1653    return isChecked;
1654}
1655
1656void
1657tr_torrentUncheck( tr_torrent * tor )
1658{
1659    tr_bitfieldRemRange ( tor->checkedPieces, 0, tor->info.pieceCount );
1660}
1661
1662int
1663tr_torrentCountUncheckedPieces( const tr_torrent * tor )
1664{
1665    return tor->info.pieceCount - tr_bitfieldCountTrueBits(
1666               tor->checkedPieces );
1667}
1668
1669time_t*
1670tr_torrentGetMTimes( const tr_torrent * tor,
1671                     size_t *           setme_n )
1672{
1673    size_t       i;
1674    const size_t n = tor->info.fileCount;
1675    time_t *     m = tr_new0( time_t, n );
1676
1677    for( i = 0; i < n; ++i )
1678    {
1679        struct stat sb;
1680        char * path = tr_buildPath( tor->downloadDir, tor->info.files[i].name, NULL );
1681        if( !stat( path, &sb ) )
1682        {
1683#ifdef SYS_DARWIN
1684            m[i] = sb.st_mtimespec.tv_sec;
1685#else
1686            m[i] = sb.st_mtime;
1687#endif
1688        }
1689        tr_free( path );
1690    }
1691
1692    *setme_n = n;
1693    return m;
1694}
1695
1696/***
1697****
1698***/
1699
1700void
1701tr_torrentSetAnnounceList( tr_torrent *            tor,
1702                           const tr_tracker_info * trackers,
1703                           int                     trackerCount )
1704{
1705    tr_benc metainfo;
1706
1707    /* save to the .torrent file */
1708    if( !tr_bencLoadFile( tor->info.torrent, &metainfo ) )
1709    {
1710        int       i;
1711        int       prevTier = -1;
1712        tr_benc * tier = NULL;
1713        tr_benc * announceList;
1714        tr_info   tmpInfo;
1715
1716        /* remove the old fields */
1717        tr_bencDictRemove( &metainfo, "announce" );
1718        tr_bencDictRemove( &metainfo, "announce-list" );
1719
1720        /* add the new fields */
1721        tr_bencDictAddStr( &metainfo, "announce", trackers[0].announce );
1722        announceList = tr_bencDictAddList( &metainfo, "announce-list", 0 );
1723        for( i = 0; i < trackerCount; ++i ) {
1724            if( prevTier != trackers[i].tier ) {
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            /* it's good, so keep these new trackers and free the old ones */
1736
1737            tr_info swap;
1738            swap.trackers = tor->info.trackers;
1739            swap.trackerCount = tor->info.trackerCount;
1740            tor->info.trackers = tmpInfo.trackers;
1741            tor->info.trackerCount = tmpInfo.trackerCount;
1742            tmpInfo.trackers = swap.trackers;
1743            tmpInfo.trackerCount = swap.trackerCount;
1744
1745            tr_metainfoFree( &tmpInfo );
1746            tr_bencSaveFile( tor->info.torrent, &metainfo );
1747        }
1748
1749        /* cleanup */
1750        tr_bencFree( &metainfo );
1751    }
1752}
1753
1754/**
1755***
1756**/
1757
1758/** @deprecated this method will be removed in 1.40 */
1759void
1760tr_torrentSetAddedDate( tr_torrent * tor,
1761                        time_t       t )
1762{
1763    tor->addedDate = t;
1764}
1765
1766/** @deprecated this method will be removed in 1.40 */
1767void
1768tr_torrentSetActivityDate( tr_torrent * tor,
1769                           time_t       t )
1770{
1771    tor->activityDate = t;
1772}
1773
1774/** @deprecated this method will be removed in 1.40 */
1775void
1776tr_torrentSetDoneDate( tr_torrent * tor,
1777                       time_t       t )
1778{
1779    tor->doneDate = t;
1780}
1781
1782/**
1783***
1784**/
1785
1786uint64_t
1787tr_torrentGetBytesLeftToAllocate( const tr_torrent * tor )
1788{
1789    const tr_file * it;
1790    const tr_file * end;
1791    struct stat sb;
1792    uint64_t bytesLeft = 0;
1793
1794    for( it=tor->info.files, end=it+tor->info.fileCount; it!=end; ++it )
1795    {
1796        if( !it->dnd )
1797        {
1798            char * path = tr_buildPath( tor->downloadDir, it->name, NULL );
1799
1800            bytesLeft += it->length;
1801
1802            if( !stat( path, &sb )
1803                    && S_ISREG( sb.st_mode )
1804                    && ( (uint64_t)sb.st_size <= it->length ) )
1805                bytesLeft -= sb.st_size;
1806
1807            tr_free( path );
1808        }
1809    }
1810
1811    return bytesLeft;
1812}
Note: See TracBrowser for help on using the repository browser.