source: trunk/libtransmission/torrent.c @ 7687

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

(trunk libT) #1427: Files are kept open in read/write mode after a torrent finishes

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