source: branches/1.5x/libtransmission/torrent.c @ 8204

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

(1.5x libT) various backports for 1.52:
(1) recognize Aria2 as a client
(2) remove jhujhiti's tr_suspectAddress(), since he removed it from trunka
(3) on Mac, better detection of where the Web UI files are located
(4) reintroduce the web task queue
(5) various minor formatting changes to reduce the diffs between 1.52 and trunk

  • Property svn:keywords set to Date Rev Author Id
File size: 50.3 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 8204 2009-04-10 17:34:25Z 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( tr_isTorrent( tor ) );
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( tr_isTorrent( tor ) );
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, TR_PEER_FROM_TRACKER, pex + i );
209            }
210
211            tr_free( pex );
212            break;
213        }
214
215        case TR_TRACKER_WARNING:
216            tr_torerr( tor, _( "Tracker warning: \"%s\"" ), event->text );
217            tor->error = -1;
218            tr_strlcpy( tor->errorString, event->text,
219                       sizeof( tor->errorString ) );
220            break;
221
222        case TR_TRACKER_ERROR:
223            tr_torerr( tor, _( "Tracker error: \"%s\"" ), event->text );
224            tor->error = -2;
225            tr_strlcpy( tor->errorString, event->text,
226                       sizeof( tor->errorString ) );
227            break;
228
229        case TR_TRACKER_ERROR_CLEAR:
230            tor->error = 0;
231            tor->errorString[0] = '\0';
232            break;
233    }
234}
235
236/***
237****
238****  TORRENT INSTANTIATION
239****
240***/
241
242static int
243getBytePiece( const tr_info * info,
244              uint64_t        byteOffset )
245{
246    assert( info );
247    assert( info->pieceSize != 0 );
248
249    return byteOffset / info->pieceSize;
250}
251
252static void
253initFilePieces( tr_info *       info,
254                tr_file_index_t fileIndex )
255{
256    tr_file * file;
257    uint64_t  firstByte, lastByte;
258
259    assert( info );
260    assert( fileIndex < info->fileCount );
261
262    file = &info->files[fileIndex];
263    firstByte = file->offset;
264    lastByte = firstByte + ( file->length ? file->length - 1 : 0 );
265    file->firstPiece = getBytePiece( info, firstByte );
266    file->lastPiece = getBytePiece( info, lastByte );
267}
268
269static int
270pieceHasFile( tr_piece_index_t piece,
271              const tr_file *  file )
272{
273    return ( file->firstPiece <= piece ) && ( piece <= file->lastPiece );
274}
275
276static tr_priority_t
277calculatePiecePriority( const tr_torrent * tor,
278                        tr_piece_index_t   piece,
279                        int                fileHint )
280{
281    tr_file_index_t i;
282    int             priority = TR_PRI_LOW;
283
284    /* find the first file that has data in this piece */
285    if( fileHint >= 0 )
286    {
287        i = fileHint;
288        while( i > 0 && pieceHasFile( piece, &tor->info.files[i - 1] ) )
289            --i;
290    }
291    else
292    {
293        for( i = 0; i < tor->info.fileCount; ++i )
294            if( pieceHasFile( piece, &tor->info.files[i] ) )
295                break;
296    }
297
298    /* the piece's priority is the max of the priorities
299     * of all the files in that piece */
300    for( ; i < tor->info.fileCount; ++i )
301    {
302        const tr_file * file = &tor->info.files[i];
303
304        if( !pieceHasFile( piece, file ) )
305            break;
306
307        priority = MAX( priority, file->priority );
308
309        /* when dealing with multimedia files, getting the first and
310           last pieces can sometimes allow you to preview it a bit
311           before it's fully downloaded... */
312        if( file->priority >= TR_PRI_NORMAL )
313            if( file->firstPiece == piece || file->lastPiece == piece )
314                priority = TR_PRI_HIGH;
315    }
316
317    return priority;
318}
319
320static void
321tr_torrentInitFilePieces( tr_torrent * tor )
322{
323    tr_file_index_t  ff;
324    tr_piece_index_t pp;
325    uint64_t         offset = 0;
326    tr_info *        inf = &tor->info;
327
328    assert( inf );
329
330    for( ff = 0; ff < inf->fileCount; ++ff )
331    {
332        inf->files[ff].offset = offset;
333        offset += inf->files[ff].length;
334        initFilePieces( inf, ff );
335    }
336
337    for( pp = 0; pp < inf->pieceCount; ++pp )
338        inf->pieces[pp].priority = calculatePiecePriority( tor, pp, -1 );
339}
340
341int
342tr_torrentPromoteTracker( tr_torrent * tor,
343                          int          pos )
344{
345    int i;
346    int tier;
347
348    assert( tor );
349    assert( ( 0 <= pos ) && ( pos < tor->info.trackerCount ) );
350
351    /* the tier of the tracker we're promoting */
352    tier = tor->info.trackers[pos].tier;
353
354    /* find the index of that tier's first tracker */
355    for( i = 0; i < tor->info.trackerCount; ++i )
356        if( tor->info.trackers[i].tier == tier )
357            break;
358
359    assert( i < tor->info.trackerCount );
360
361    /* promote the tracker at `pos' to the front of the tier */
362    if( i != pos )
363    {
364        const tr_tracker_info tmp = tor->info.trackers[i];
365        tor->info.trackers[i] = tor->info.trackers[pos];
366        tor->info.trackers[pos] = tmp;
367    }
368
369    /* return the new position of the tracker that started out at [pos] */
370    return i;
371}
372
373struct RandomTracker
374{
375    tr_tracker_info    tracker;
376    int                random_value;
377};
378
379/* the tiers will be sorted from lowest to highest,
380 * and trackers are randomized within the tiers */
381static TR_INLINE int
382compareRandomTracker( const void * va,
383                      const void * vb )
384{
385    const struct RandomTracker * a = va;
386    const struct RandomTracker * b = vb;
387
388    if( a->tracker.tier != b->tracker.tier )
389        return a->tracker.tier - b->tracker.tier;
390
391    return a->random_value - b->random_value;
392}
393
394static void
395randomizeTiers( tr_info * info )
396{
397    int                    i;
398    const int              n = info->trackerCount;
399    struct RandomTracker * r = tr_new0( struct RandomTracker, n );
400
401    for( i = 0; i < n; ++i )
402    {
403        r[i].tracker = info->trackers[i];
404        r[i].random_value = tr_cryptoRandInt( INT_MAX );
405    }
406    qsort( r, n, sizeof( struct RandomTracker ), compareRandomTracker );
407    for( i = 0; i < n; ++i )
408        info->trackers[i] = r[i].tracker;
409    tr_free( r );
410}
411
412static void torrentStart( tr_torrent * tor,
413                          int          reloadProgress );
414
415/**
416 * Decide on a block size.  constraints:
417 * (1) most clients decline requests over 16 KiB
418 * (2) pieceSize must be a multiple of block size
419 */
420static uint32_t
421getBlockSize( uint32_t pieceSize )
422{
423    uint32_t b = pieceSize;
424
425    while( b > MAX_BLOCK_SIZE )
426        b /= 2u;
427
428    if( !b || ( pieceSize % b ) ) /* not cleanly divisible */
429        return 0;
430    return b;
431}
432
433static void
434torrentRealInit( tr_session      * session,
435                 tr_torrent      * tor,
436                 const tr_ctor   * ctor )
437{
438    int          doStart;
439    uint64_t     loaded;
440    uint64_t     t;
441    const char * dir;
442    static int   nextUniqueId = 1;
443    tr_info    * info = &tor->info;
444
445    tr_globalLock( session );
446
447    tor->session   = session;
448    tor->uniqueId = nextUniqueId++;
449    tor->magicNumber = TORRENT_MAGIC_NUMBER;
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    assert( tr_isTorrent( tor  ) );
638
639    if( !path || !tor->downloadDir || strcmp( path, tor->downloadDir ) )
640    {
641        tr_free( tor->downloadDir );
642        tor->downloadDir = tr_strdup( path );
643        tr_torrentSaveResume( tor );
644    }
645}
646
647const char*
648tr_torrentGetDownloadDir( const tr_torrent * tor )
649{
650    assert( tr_isTorrent( tor  ) );
651
652    return tor->downloadDir;
653}
654
655void
656tr_torrentChangeMyPort( tr_torrent * tor )
657{
658    assert( tr_isTorrent( tor  ) );
659
660    if( tor->tracker )
661        tr_trackerChangeMyPort( tor->tracker );
662}
663
664static TR_INLINE void
665tr_torrentManualUpdateImpl( void * vtor )
666{
667    tr_torrent * tor = vtor;
668
669    assert( tr_isTorrent( tor  ) );
670
671    if( tor->isRunning )
672        tr_trackerReannounce( tor->tracker );
673}
674
675void
676tr_torrentManualUpdate( tr_torrent * tor )
677{
678    assert( tr_isTorrent( tor  ) );
679
680    tr_runInEventThread( tor->session, tr_torrentManualUpdateImpl, tor );
681}
682
683int
684tr_torrentCanManualUpdate( const tr_torrent * tor )
685{
686    return ( tr_isTorrent( tor  ) )
687        && ( tor->isRunning )
688        && ( tr_trackerCanManualAnnounce( tor->tracker ) );
689}
690
691const tr_info *
692tr_torrentInfo( const tr_torrent * tor )
693{
694    return tr_isTorrent( tor ) ? &tor->info : NULL;
695}
696
697const tr_stat *
698tr_torrentStatCached( tr_torrent * tor )
699{
700    const time_t now = time( NULL );
701
702    return tr_isTorrent( tor ) && ( now == tor->lastStatTime )
703         ? &tor->stats
704         : tr_torrentStat( tor );
705}
706
707tr_torrent_activity
708tr_torrentGetActivity( tr_torrent * tor )
709{
710    assert( tr_isTorrent( tor ) );
711
712    tr_torrentRecheckCompleteness( tor );
713
714    if( tor->verifyState == TR_VERIFY_NOW )
715        return TR_STATUS_CHECK;
716    if( tor->verifyState == TR_VERIFY_WAIT )
717        return TR_STATUS_CHECK_WAIT;
718    if( !tor->isRunning )
719        return TR_STATUS_STOPPED;
720    if( tor->completeness == TR_LEECH )
721        return TR_STATUS_DOWNLOAD;
722
723    return TR_STATUS_SEED;
724}
725
726const tr_stat *
727tr_torrentStat( tr_torrent * tor )
728{
729    tr_stat *               s;
730    struct tr_tracker *     tc;
731    const tr_tracker_info * ti;
732    int                     usableSeeds = 0;
733    uint64_t                now;
734
735    if( !tor )
736        return NULL;
737
738    assert( tr_isTorrent( tor ) );
739    tr_torrentLock( tor );
740
741    tor->lastStatTime = time( NULL );
742
743    s = &tor->stats;
744    s->id = tor->uniqueId;
745    s->activity = tr_torrentGetActivity( tor );
746    s->error  = tor->error;
747    memcpy( s->errorString, tor->errorString,
748           sizeof( s->errorString ) );
749
750    tc = tor->tracker;
751    ti = tr_trackerGetAddress( tor->tracker, tor );
752    s->announceURL = ti ? ti->announce : NULL;
753    s->scrapeURL   = ti ? ti->scrape   : NULL;
754    tr_trackerStat( tc, s );
755
756    tr_trackerGetCounts( tc, &s->timesCompleted,
757                             &s->leechers,
758                             &s->seeders,
759                             &s->downloaders );
760
761    tr_peerMgrTorrentStats( tor,
762                            &s->peersKnown,
763                            &s->peersConnected,
764                            &usableSeeds,
765                            &s->webseedsSendingToUs,
766                            &s->peersSendingToUs,
767                            &s->peersGettingFromUs,
768                            s->peersFrom );
769
770    now = tr_date( );
771    s->swarmSpeed         = tr_rcRate( &tor->swarmSpeed, now );
772    s->rawUploadSpeed     = tr_bandwidthGetRawSpeed  ( tor->bandwidth, now, TR_UP );
773    s->rawDownloadSpeed   = tr_bandwidthGetRawSpeed  ( tor->bandwidth, now, TR_DOWN );
774    s->pieceUploadSpeed   = tr_bandwidthGetPieceSpeed( tor->bandwidth, now, TR_UP );
775    s->pieceDownloadSpeed = tr_bandwidthGetPieceSpeed( tor->bandwidth, now, TR_DOWN );
776
777    usableSeeds += tor->info.webseedCount;
778
779    s->percentComplete = tr_cpPercentComplete ( &tor->completion );
780
781    s->percentDone   = tr_cpPercentDone  ( &tor->completion );
782    s->leftUntilDone = tr_cpLeftUntilDone( &tor->completion );
783    s->sizeWhenDone  = tr_cpSizeWhenDone ( &tor->completion );
784
785    s->recheckProgress = s->activity == TR_STATUS_CHECK
786                       ? 1.0 -
787                         ( tr_torrentCountUncheckedPieces( tor ) /
788                           (double) tor->info.pieceCount )
789                       : 0.0;
790
791
792    s->activityDate = tor->activityDate;
793    s->addedDate    = tor->addedDate;
794    s->doneDate     = tor->doneDate;
795    s->startDate    = tor->startDate;
796
797    s->corruptEver     = tor->corruptCur    + tor->corruptPrev;
798    s->downloadedEver  = tor->downloadedCur + tor->downloadedPrev;
799    s->uploadedEver    = tor->uploadedCur   + tor->uploadedPrev;
800    s->haveValid       = tr_cpHaveValid( &tor->completion );
801    s->haveUnchecked   = tr_cpHaveTotal( &tor->completion ) - s->haveValid;
802
803
804    if( usableSeeds > 0 )
805    {
806        s->desiredAvailable = s->leftUntilDone;
807    }
808    else if( !s->leftUntilDone || !s->peersConnected )
809    {
810        s->desiredAvailable = 0;
811    }
812    else
813    {
814        tr_piece_index_t i;
815        tr_bitfield *    peerPieces = tr_peerMgrGetAvailable( tor );
816        s->desiredAvailable = 0;
817        for( i = 0; i < tor->info.pieceCount; ++i )
818            if( !tor->info.pieces[i].dnd && tr_bitfieldHas( peerPieces, i ) )
819                s->desiredAvailable += tr_cpMissingBlocksInPiece( &tor->completion, i );
820        s->desiredAvailable *= tor->blockSize;
821        tr_bitfieldFree( peerPieces );
822    }
823
824    if( s->leftUntilDone > s->desiredAvailable )
825        s->eta = TR_ETA_NOT_AVAIL;
826    else if( s->pieceDownloadSpeed < 0.1 )
827        s->eta = TR_ETA_UNKNOWN;
828    else
829        s->eta = s->leftUntilDone / s->pieceDownloadSpeed / 1024.0;
830
831    s->ratio = tr_getRatio(
832        s->uploadedEver,
833        s->downloadedEver ? s->downloadedEver : s->
834        haveValid );
835
836    tr_torrentUnlock( tor );
837
838    return s;
839}
840
841/***
842****
843***/
844
845static uint64_t
846fileBytesCompleted( const tr_torrent * tor,
847                    tr_file_index_t    fileIndex )
848{
849    const tr_file *        file     =  &tor->info.files[fileIndex];
850    const tr_block_index_t firstBlock       =  file->offset /
851                                              tor->blockSize;
852    const uint64_t         firstBlockOffset =  file->offset %
853                                              tor->blockSize;
854    const uint64_t         lastOffset       =
855        file->length ? ( file->length - 1 ) : 0;
856    const tr_block_index_t lastBlock        =
857        ( file->offset + lastOffset ) / tor->blockSize;
858    const uint64_t         lastBlockOffset  =
859        ( file->offset + lastOffset ) % tor->blockSize;
860    uint64_t               haveBytes = 0;
861
862    assert( tr_isTorrent( tor ) );
863    assert( fileIndex < tor->info.fileCount );
864    assert( file->offset + file->length <= tor->info.totalSize );
865    assert( ( firstBlock < tor->blockCount )
866          || ( !file->length && file->offset == tor->info.totalSize ) );
867    assert( ( lastBlock < tor->blockCount )
868          || ( !file->length && file->offset == tor->info.totalSize ) );
869    assert( firstBlock <= lastBlock );
870    assert( tr_torBlockPiece( tor, firstBlock ) == file->firstPiece );
871    assert( tr_torBlockPiece( tor, lastBlock ) == file->lastPiece );
872
873    if( firstBlock == lastBlock )
874    {
875        if( tr_cpBlockIsComplete( &tor->completion, firstBlock ) )
876            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
877    }
878    else
879    {
880        tr_block_index_t i;
881
882        if( tr_cpBlockIsComplete( &tor->completion, firstBlock ) )
883            haveBytes += tor->blockSize - firstBlockOffset;
884
885        for( i = firstBlock + 1; i < lastBlock; ++i )
886            if( tr_cpBlockIsComplete( &tor->completion, i ) )
887                haveBytes += tor->blockSize;
888
889        if( tr_cpBlockIsComplete( &tor->completion, lastBlock ) )
890            haveBytes += lastBlockOffset + 1;
891    }
892
893    return haveBytes;
894}
895
896tr_file_stat *
897tr_torrentFiles( const tr_torrent * tor,
898                 tr_file_index_t *  fileCount )
899{
900    tr_file_index_t       i;
901    const tr_file_index_t n = tor->info.fileCount;
902    tr_file_stat *        files = tr_new0( tr_file_stat, n );
903    tr_file_stat *        walk = files;
904
905    assert( tr_isTorrent( tor ) );
906
907    for( i = 0; i < n; ++i, ++walk )
908    {
909        const uint64_t b = fileBytesCompleted( tor, i );
910        walk->bytesCompleted = b;
911        walk->progress = tr_getRatio( b, tor->info.files[i].length );
912    }
913
914    if( fileCount )
915        *fileCount = n;
916
917    return files;
918}
919
920void
921tr_torrentFilesFree( tr_file_stat *            files,
922                     tr_file_index_t fileCount UNUSED )
923{
924    tr_free( files );
925}
926
927/***
928****
929***/
930
931float*
932tr_torrentWebSpeeds( const tr_torrent * tor )
933{
934    return tr_isTorrent( tor )
935         ? tr_peerMgrWebSpeeds( tor )
936         : NULL;
937}
938
939tr_peer_stat *
940tr_torrentPeers( const tr_torrent * tor,
941                 int *              peerCount )
942{
943    tr_peer_stat * ret = NULL;
944
945    if( tr_isTorrent( tor ) )
946        ret = tr_peerMgrPeerStats( tor, peerCount );
947
948    return ret;
949}
950
951void
952tr_torrentPeersFree( tr_peer_stat * peers,
953                     int peerCount  UNUSED )
954{
955    tr_free( peers );
956}
957
958void
959tr_torrentAvailability( const tr_torrent * tor,
960                        int8_t *           tab,
961                        int                size )
962{
963    tr_peerMgrTorrentAvailability( tor, tab, size );
964}
965
966void
967tr_torrentAmountFinished( const tr_torrent * tor,
968                          float *            tab,
969                          int                size )
970{
971    assert( tr_isTorrent( tor ) );
972
973    tr_torrentLock( tor );
974    tr_cpGetAmountDone( &tor->completion, tab, size );
975    tr_torrentUnlock( tor );
976}
977
978void
979tr_torrentResetTransferStats( tr_torrent * tor )
980{
981    assert( tr_isTorrent( tor ) );
982
983    tr_torrentLock( tor );
984
985    tor->downloadedPrev += tor->downloadedCur;
986    tor->downloadedCur   = 0;
987    tor->uploadedPrev   += tor->uploadedCur;
988    tor->uploadedCur     = 0;
989    tor->corruptPrev    += tor->corruptCur;
990    tor->corruptCur      = 0;
991
992    tr_torrentUnlock( tor );
993}
994
995void
996tr_torrentSetHasPiece( tr_torrent *     tor,
997                       tr_piece_index_t pieceIndex,
998                       tr_bool          has )
999{
1000    assert( tr_isTorrent( tor ) );
1001    assert( pieceIndex < tor->info.pieceCount );
1002
1003    if( has )
1004        tr_cpPieceAdd( &tor->completion, pieceIndex );
1005    else
1006        tr_cpPieceRem( &tor->completion, pieceIndex );
1007}
1008
1009/***
1010****
1011***/
1012
1013static void
1014freeTorrent( tr_torrent * tor )
1015{
1016    tr_torrent * t;
1017    tr_session *  session = tor->session;
1018    tr_info *    inf = &tor->info;
1019
1020    assert( tr_isTorrent( tor ) );
1021    assert( !tor->isRunning );
1022
1023    tr_globalLock( session );
1024
1025    tr_peerMgrRemoveTorrent( tor );
1026
1027    tr_cpDestruct( &tor->completion );
1028
1029    tr_rcDestruct( &tor->swarmSpeed );
1030
1031    tr_trackerUnsubscribe( tor->tracker, tor->trackerSubscription );
1032    tr_trackerFree( tor->tracker );
1033    tor->tracker = NULL;
1034
1035    tr_bitfieldDestruct( &tor->checkedPieces );
1036
1037    tr_free( tor->downloadDir );
1038    tr_free( tor->peer_id );
1039
1040    if( tor == session->torrentList )
1041        session->torrentList = tor->next;
1042    else for( t = session->torrentList; t != NULL; t = t->next ) {
1043        if( t->next == tor ) {
1044            t->next = tor->next;
1045            break;
1046        }
1047    }
1048
1049    assert( session->torrentCount >= 1 );
1050    session->torrentCount--;
1051
1052    tr_bandwidthFree( tor->bandwidth );
1053
1054    tr_metainfoFree( inf );
1055    tr_free( tor );
1056
1057    tr_globalUnlock( session );
1058}
1059
1060/**
1061***  Start/Stop Callback
1062**/
1063
1064static void
1065checkAndStartImpl( void * vtor )
1066{
1067    tr_torrent * tor = vtor;
1068
1069    assert( tr_isTorrent( tor ) );
1070
1071    tr_globalLock( tor->session );
1072
1073    tor->isRunning = TRUE;
1074    *tor->errorString = '\0';
1075    tr_torrentResetTransferStats( tor );
1076    tor->completeness = tr_cpGetStatus( &tor->completion );
1077    tr_torrentSaveResume( tor );
1078    tor->startDate = time( NULL );
1079    tr_trackerStart( tor->tracker );
1080    tr_peerMgrStartTorrent( tor );
1081
1082    tr_globalUnlock( tor->session );
1083}
1084
1085static void
1086checkAndStartCB( tr_torrent * tor )
1087{
1088    assert( tr_isTorrent( tor ) );
1089    assert( tr_isSession( tor->session ) );
1090
1091    tr_runInEventThread( tor->session, checkAndStartImpl, tor );
1092}
1093
1094static void
1095torrentStart( tr_torrent * tor,
1096              int          reloadProgress )
1097{
1098    assert( tr_isTorrent( tor ) );
1099
1100    tr_globalLock( tor->session );
1101
1102    if( !tor->isRunning )
1103    {
1104        const int isVerifying = tr_verifyInProgress( tor );
1105
1106        if( !isVerifying && reloadProgress )
1107            tr_torrentLoadResume( tor, TR_FR_PROGRESS, NULL );
1108
1109        tor->isRunning = 1;
1110
1111        if( !isVerifying )
1112            tr_verifyAdd( tor, checkAndStartCB );
1113    }
1114
1115    tr_globalUnlock( tor->session );
1116}
1117
1118void
1119tr_torrentStart( tr_torrent * tor )
1120{
1121    if( tr_isTorrent( tor ) )
1122        torrentStart( tor, TRUE );
1123}
1124
1125static void
1126torrentRecheckDoneImpl( void * vtor )
1127{
1128    tr_torrent * tor = vtor;
1129
1130    assert( tr_isTorrent( tor ) );
1131    tr_torrentRecheckCompleteness( tor );
1132}
1133
1134static void
1135torrentRecheckDoneCB( tr_torrent * tor )
1136{
1137    assert( tr_isTorrent( tor ) );
1138
1139    tr_runInEventThread( tor->session, torrentRecheckDoneImpl, tor );
1140}
1141
1142void
1143tr_torrentVerify( tr_torrent * tor )
1144{
1145    assert( tr_isTorrent( tor ) );
1146
1147    tr_verifyRemove( tor );
1148
1149    tr_globalLock( tor->session );
1150
1151    tr_torrentUncheck( tor );
1152    tr_verifyAdd( tor, torrentRecheckDoneCB );
1153
1154    tr_globalUnlock( tor->session );
1155}
1156
1157static void
1158tr_torrentCloseLocalFiles( const tr_torrent * tor )
1159{
1160    tr_file_index_t i;
1161    struct evbuffer * buf = evbuffer_new( );
1162
1163    assert( tr_isTorrent( tor ) );
1164
1165    for( i=0; i<tor->info.fileCount; ++i )
1166    {
1167        const tr_file * file = &tor->info.files[i];
1168        evbuffer_drain( buf, EVBUFFER_LENGTH( buf ) );
1169        evbuffer_add_printf( buf, "%s%s%s", tor->downloadDir, TR_PATH_DELIMITER_STR, file->name );
1170        tr_fdFileClose( (const char*) EVBUFFER_DATA( buf ) );
1171    }
1172
1173    evbuffer_free( buf );
1174}
1175
1176
1177static void
1178stopTorrent( void * vtor )
1179{
1180    tr_torrent * tor = vtor;
1181
1182    assert( tr_isTorrent( tor ) );
1183
1184    tr_verifyRemove( tor );
1185    tr_peerMgrStopTorrent( tor );
1186    tr_trackerStop( tor->tracker );
1187
1188    tr_torrentCloseLocalFiles( tor );
1189}
1190
1191void
1192tr_torrentStop( tr_torrent * tor )
1193{
1194    if( tr_isTorrent( tor ) )
1195    {
1196        tr_globalLock( tor->session );
1197
1198        tor->isRunning = 0;
1199        if( !tor->isDeleting )
1200            tr_torrentSaveResume( tor );
1201        tr_runInEventThread( tor->session, stopTorrent, tor );
1202
1203        tr_globalUnlock( tor->session );
1204    }
1205}
1206
1207static void
1208closeTorrent( void * vtor )
1209{
1210    tr_torrent * tor = vtor;
1211
1212    assert( tr_isTorrent( tor ) );
1213
1214    tr_torrentSaveResume( tor );
1215    tor->isRunning = 0;
1216    stopTorrent( tor );
1217    if( tor->isDeleting )
1218    {
1219        tr_metainfoRemoveSaved( tor->session, &tor->info );
1220        tr_torrentRemoveResume( tor );
1221    }
1222    freeTorrent( tor );
1223}
1224
1225void
1226tr_torrentFree( tr_torrent * tor )
1227{
1228    if( tr_isTorrent( tor ) )
1229    {
1230        tr_session * session = tor->session;
1231        assert( tr_isSession( session ) );
1232        tr_globalLock( session );
1233
1234        tr_torrentClearCompletenessCallback( tor );
1235        tr_runInEventThread( session, closeTorrent, tor );
1236
1237        tr_globalUnlock( session );
1238    }
1239}
1240
1241void
1242tr_torrentRemove( tr_torrent * tor )
1243{
1244    assert( tr_isTorrent( tor ) );
1245
1246    tor->isDeleting = 1;
1247    tr_torrentFree( tor );
1248}
1249
1250/**
1251***  Completeness
1252**/
1253
1254static const char *
1255getCompletionString( int type )
1256{
1257    switch( type )
1258    {
1259        /* Translators: this is a minor point that's safe to skip over, but FYI:
1260           "Complete" and "Done" are specific, different terms in Transmission:
1261           "Complete" means we've downloaded every file in the torrent.
1262           "Done" means we're done downloading the files we wanted, but NOT all
1263           that exist */
1264        case TR_PARTIAL_SEED:
1265            return _( "Done" );
1266
1267        case TR_SEED:
1268            return _( "Complete" );
1269
1270        default:
1271            return _( "Incomplete" );
1272    }
1273}
1274
1275static void
1276fireCompletenessChange( tr_torrent       * tor,
1277                        tr_completeness    status )
1278{
1279    assert( tr_isTorrent( tor ) );
1280    assert( ( status == TR_LEECH )
1281         || ( status == TR_SEED )
1282         || ( status == TR_PARTIAL_SEED ) );
1283
1284    if( tor->completeness_func )
1285        tor->completeness_func( tor, status, tor->completeness_func_user_data );
1286}
1287
1288void
1289tr_torrentSetCompletenessCallback( tr_torrent                    * tor,
1290                                   tr_torrent_completeness_func    func,
1291                                   void                          * user_data )
1292{
1293    assert( tr_isTorrent( tor ) );
1294
1295    tor->completeness_func = func;
1296    tor->completeness_func_user_data = user_data;
1297}
1298
1299void
1300tr_torrentClearCompletenessCallback( tr_torrent * torrent )
1301{
1302    tr_torrentSetCompletenessCallback( torrent, NULL, NULL );
1303}
1304
1305void
1306tr_torrentRecheckCompleteness( tr_torrent * tor )
1307{
1308    tr_completeness completeness;
1309
1310    assert( tr_isTorrent( tor ) );
1311
1312    tr_torrentLock( tor );
1313
1314    completeness = tr_cpGetStatus( &tor->completion );
1315
1316    if( completeness != tor->completeness )
1317    {
1318        const int recentChange = tor->downloadedCur != 0;
1319
1320        if( recentChange )
1321        {
1322            tr_torinf( tor, _( "State changed from \"%1$s\" to \"%2$s\"" ),
1323                      getCompletionString( tor->completeness ),
1324                      getCompletionString( completeness ) );
1325        }
1326
1327        tor->completeness = completeness;
1328        tr_torrentCloseLocalFiles( tor );
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
1344/**
1345***  File priorities
1346**/
1347
1348void
1349tr_torrentInitFilePriority( tr_torrent *    tor,
1350                            tr_file_index_t fileIndex,
1351                            tr_priority_t   priority )
1352{
1353    tr_piece_index_t i;
1354    tr_file *        file;
1355
1356    assert( tr_isTorrent( tor ) );
1357    assert( fileIndex < tor->info.fileCount );
1358    assert( priority == TR_PRI_LOW || priority == TR_PRI_NORMAL || priority == TR_PRI_HIGH );
1359
1360    file = &tor->info.files[fileIndex];
1361    file->priority = priority;
1362    for( i = file->firstPiece; i <= file->lastPiece; ++i )
1363        tor->info.pieces[i].priority = calculatePiecePriority( tor, i,
1364                                                               fileIndex );
1365}
1366
1367void
1368tr_torrentSetFilePriorities( tr_torrent *      tor,
1369                             tr_file_index_t * files,
1370                             tr_file_index_t   fileCount,
1371                             tr_priority_t     priority )
1372{
1373    tr_file_index_t i;
1374
1375    assert( tr_isTorrent( tor ) );
1376
1377    tr_torrentLock( tor );
1378
1379    for( i = 0; i < fileCount; ++i )
1380        tr_torrentInitFilePriority( tor, files[i], priority );
1381
1382    tr_torrentSaveResume( tor );
1383    tr_torrentUnlock( tor );
1384}
1385
1386tr_priority_t
1387tr_torrentGetFilePriority( const tr_torrent * tor,
1388                           tr_file_index_t    file )
1389{
1390    tr_priority_t ret;
1391
1392    assert( tr_isTorrent( tor ) );
1393
1394    tr_torrentLock( tor );
1395    assert( tor );
1396    assert( file < tor->info.fileCount );
1397    ret = tor->info.files[file].priority;
1398    tr_torrentUnlock( tor );
1399
1400    return ret;
1401}
1402
1403tr_priority_t*
1404tr_torrentGetFilePriorities( const tr_torrent * tor )
1405{
1406    tr_file_index_t i;
1407    tr_priority_t * p;
1408
1409    assert( tr_isTorrent( tor ) );
1410
1411    tr_torrentLock( tor );
1412    p = tr_new0( tr_priority_t, tor->info.fileCount );
1413    for( i = 0; i < tor->info.fileCount; ++i )
1414        p[i] = tor->info.files[i].priority;
1415    tr_torrentUnlock( tor );
1416
1417    return p;
1418}
1419
1420/**
1421***  File DND
1422**/
1423
1424int
1425tr_torrentGetFileDL( const tr_torrent * tor,
1426                     tr_file_index_t    file )
1427{
1428    int doDownload;
1429
1430    assert( tr_isTorrent( tor ) );
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, tr_file_index_t fileIndex, int doDownload )
1443{
1444    tr_file *        file;
1445    const int        dnd = !doDownload;
1446    tr_piece_index_t firstPiece, firstPieceDND;
1447    tr_piece_index_t lastPiece, lastPieceDND;
1448    tr_file_index_t  i;
1449
1450    assert( tr_isTorrent( tor ) );
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                       tr_bool           doDownload )
1501{
1502    tr_file_index_t i;
1503
1504    assert( tr_isTorrent( tor ) );
1505
1506    tr_torrentLock( tor );
1507
1508    for( i=0; i<fileCount; ++i )
1509        setFileDND( tor, files[i], doDownload );
1510    tr_cpInvalidateDND ( &tor->completion );
1511
1512    tr_torrentUnlock( tor );
1513}
1514
1515void
1516tr_torrentSetFileDLs( tr_torrent *      tor,
1517                      tr_file_index_t * files,
1518                      tr_file_index_t   fileCount,
1519                      tr_bool           doDownload )
1520{
1521    assert( tr_isTorrent( tor ) );
1522
1523    tr_torrentLock( tor );
1524    tr_torrentInitFileDLs( tor, files, fileCount, doDownload );
1525    tr_torrentSaveResume( tor );
1526    tr_torrentUnlock( tor );
1527}
1528
1529/***
1530****
1531***/
1532
1533void
1534tr_torrentSetPeerLimit( tr_torrent * tor,
1535                        uint16_t     maxConnectedPeers )
1536{
1537    assert( tr_isTorrent( tor ) );
1538
1539    tor->maxConnectedPeers = maxConnectedPeers;
1540}
1541
1542uint16_t
1543tr_torrentGetPeerLimit( const tr_torrent * tor )
1544{
1545    assert( tr_isTorrent( tor ) );
1546
1547    return tor->maxConnectedPeers;
1548}
1549
1550/***
1551****
1552***/
1553
1554tr_block_index_t
1555_tr_block( const tr_torrent * tor,
1556           tr_piece_index_t   index,
1557           uint32_t           offset )
1558{
1559    tr_block_index_t ret;
1560
1561    assert( tr_isTorrent( tor ) );
1562
1563    ret = index;
1564    ret *= ( tor->info.pieceSize / tor->blockSize );
1565    ret += offset / tor->blockSize;
1566    return ret;
1567}
1568
1569tr_bool
1570tr_torrentReqIsValid( const tr_torrent * tor,
1571                      tr_piece_index_t   index,
1572                      uint32_t           offset,
1573                      uint32_t           length )
1574{
1575    int err = 0;
1576
1577    assert( tr_isTorrent( tor ) );
1578
1579    if( index >= tor->info.pieceCount )
1580        err = 1;
1581    else if( length < 1 )
1582        err = 2;
1583    else if( ( offset + length ) > tr_torPieceCountBytes( tor, index ) )
1584        err = 3;
1585    else if( length > MAX_BLOCK_SIZE )
1586        err = 4;
1587    else if( tr_pieceOffset( tor, index, offset, length ) > tor->info.totalSize )
1588        err = 5;
1589
1590    if( err ) fprintf( stderr, "index %lu offset %lu length %lu err %d\n",
1591                       (unsigned long)index, (unsigned long)offset,
1592                       (unsigned long)length,
1593                       err );
1594
1595    return !err;
1596}
1597
1598uint64_t
1599tr_pieceOffset( const tr_torrent * tor,
1600                tr_piece_index_t   index,
1601                uint32_t           offset,
1602                uint32_t           length )
1603{
1604    uint64_t ret;
1605
1606    assert( tr_isTorrent( tor ) );
1607
1608    ret = tor->info.pieceSize;
1609    ret *= index;
1610    ret += offset;
1611    ret += length;
1612    return ret;
1613}
1614
1615/***
1616****
1617***/
1618
1619void
1620tr_torrentSetPieceChecked( tr_torrent        * tor,
1621                           tr_piece_index_t    piece,
1622                           tr_bool             isChecked )
1623{
1624    assert( tr_isTorrent( tor ) );
1625
1626    if( isChecked )
1627        tr_bitfieldAdd( &tor->checkedPieces, piece );
1628    else
1629        tr_bitfieldRem( &tor->checkedPieces, piece );
1630}
1631
1632void
1633tr_torrentSetFileChecked( tr_torrent *    tor,
1634                          tr_file_index_t fileIndex,
1635                          tr_bool         isChecked )
1636{
1637    const tr_file *        file = &tor->info.files[fileIndex];
1638    const tr_piece_index_t begin = file->firstPiece;
1639    const tr_piece_index_t end = file->lastPiece + 1;
1640
1641    assert( tr_isTorrent( tor ) );
1642
1643    if( isChecked )
1644        tr_bitfieldAddRange( &tor->checkedPieces, begin, end );
1645    else
1646        tr_bitfieldRemRange( &tor->checkedPieces, begin, end );
1647}
1648
1649tr_bool
1650tr_torrentIsFileChecked( const tr_torrent * tor,
1651                         tr_file_index_t    fileIndex )
1652{
1653    const tr_file *        file = &tor->info.files[fileIndex];
1654    const tr_piece_index_t begin = file->firstPiece;
1655    const tr_piece_index_t end = file->lastPiece + 1;
1656    tr_piece_index_t       i;
1657    tr_bool                isChecked = TRUE;
1658
1659    assert( tr_isTorrent( tor ) );
1660
1661    for( i = begin; isChecked && i < end; ++i )
1662        if( !tr_torrentIsPieceChecked( tor, i ) )
1663            isChecked = FALSE;
1664
1665    return isChecked;
1666}
1667
1668void
1669tr_torrentUncheck( tr_torrent * tor )
1670{
1671    assert( tr_isTorrent( tor ) );
1672
1673    tr_bitfieldRemRange ( &tor->checkedPieces, 0, tor->info.pieceCount );
1674}
1675
1676int
1677tr_torrentCountUncheckedPieces( const tr_torrent * tor )
1678{
1679    assert( tr_isTorrent( tor ) );
1680
1681    return tor->info.pieceCount - tr_bitfieldCountTrueBits( &tor->checkedPieces );
1682}
1683
1684time_t*
1685tr_torrentGetMTimes( const tr_torrent * tor,
1686                     size_t *           setme_n )
1687{
1688    size_t       i;
1689    const size_t n = tor->info.fileCount;
1690    time_t *     m = tr_new0( time_t, n );
1691
1692    assert( tr_isTorrent( tor ) );
1693
1694    for( i = 0; i < n; ++i )
1695    {
1696        struct stat sb;
1697        char * path = tr_buildPath( tor->downloadDir, tor->info.files[i].name, NULL );
1698        if( !stat( path, &sb ) )
1699        {
1700#ifdef SYS_DARWIN
1701            m[i] = sb.st_mtimespec.tv_sec;
1702#else
1703            m[i] = sb.st_mtime;
1704#endif
1705        }
1706        tr_free( path );
1707    }
1708
1709    *setme_n = n;
1710    return m;
1711}
1712
1713/***
1714****
1715***/
1716
1717void
1718tr_torrentSetAnnounceList( tr_torrent *            tor,
1719                           const tr_tracker_info * trackers,
1720                           int                     trackerCount )
1721{
1722    tr_benc metainfo;
1723
1724    assert( tr_isTorrent( tor ) );
1725
1726    /* save to the .torrent file */
1727    if( !tr_bencLoadFile( tor->info.torrent, &metainfo ) )
1728    {
1729        int       i;
1730        int       prevTier = -1;
1731        tr_benc * tier = NULL;
1732        tr_benc * announceList;
1733        tr_info   tmpInfo;
1734
1735        /* remove the old fields */
1736        tr_bencDictRemove( &metainfo, "announce" );
1737        tr_bencDictRemove( &metainfo, "announce-list" );
1738
1739        /* add the new fields */
1740        tr_bencDictAddStr( &metainfo, "announce", trackers[0].announce );
1741        announceList = tr_bencDictAddList( &metainfo, "announce-list", 0 );
1742        for( i = 0; i < trackerCount; ++i ) {
1743            if( prevTier != trackers[i].tier ) {
1744                prevTier = trackers[i].tier;
1745                tier = tr_bencListAddList( announceList, 0 );
1746            }
1747            tr_bencListAddStr( tier, trackers[i].announce );
1748        }
1749
1750        /* try to parse it back again, to make sure it's good */
1751        memset( &tmpInfo, 0, sizeof( tr_info ) );
1752        if( !tr_metainfoParse( tor->session, &tmpInfo, &metainfo ) )
1753        {
1754            /* it's good, so keep these new trackers and free the old ones */
1755
1756            tr_info swap;
1757            swap.trackers = tor->info.trackers;
1758            swap.trackerCount = tor->info.trackerCount;
1759            tor->info.trackers = tmpInfo.trackers;
1760            tor->info.trackerCount = tmpInfo.trackerCount;
1761            tmpInfo.trackers = swap.trackers;
1762            tmpInfo.trackerCount = swap.trackerCount;
1763
1764            tr_metainfoFree( &tmpInfo );
1765            tr_bencSaveFile( tor->info.torrent, &metainfo );
1766        }
1767
1768        /* cleanup */
1769        tr_bencFree( &metainfo );
1770    }
1771}
1772
1773/**
1774***
1775**/
1776
1777void
1778tr_torrentSetAddedDate( tr_torrent * tor,
1779                        time_t       t )
1780{
1781    assert( tr_isTorrent( tor ) );
1782
1783    tor->addedDate = t;
1784}
1785
1786void
1787tr_torrentSetActivityDate( tr_torrent * tor,
1788                           time_t       t )
1789{
1790    assert( tr_isTorrent( tor ) );
1791
1792    tor->activityDate = t;
1793}
1794
1795void
1796tr_torrentSetDoneDate( tr_torrent * tor,
1797                       time_t       t )
1798{
1799    assert( tr_isTorrent( tor ) );
1800
1801    tor->doneDate = t;
1802}
1803
1804/**
1805***
1806**/
1807
1808uint64_t
1809tr_torrentGetBytesLeftToAllocate( const tr_torrent * tor )
1810{
1811    const tr_file * it;
1812    const tr_file * end;
1813    struct stat sb;
1814    uint64_t bytesLeft = 0;
1815
1816    assert( tr_isTorrent( tor ) );
1817
1818    for( it=tor->info.files, end=it+tor->info.fileCount; it!=end; ++it )
1819    {
1820        if( !it->dnd )
1821        {
1822            char * path = tr_buildPath( tor->downloadDir, it->name, NULL );
1823
1824            bytesLeft += it->length;
1825
1826            if( !stat( path, &sb )
1827                    && S_ISREG( sb.st_mode )
1828                    && ( (uint64_t)sb.st_size <= it->length ) )
1829                bytesLeft -= sb.st_size;
1830
1831            tr_free( path );
1832        }
1833    }
1834
1835    return bytesLeft;
1836}
1837
1838/****
1839*****  Removing the torrent's local data
1840****/
1841
1842static int
1843vstrcmp( const void * a, const void * b )
1844{
1845    return strcmp( a, b );
1846}
1847
1848static int
1849compareLongestFirst( const void * a, const void * b )
1850{
1851    const size_t alen = strlen( a );
1852    const size_t blen = strlen( b );
1853
1854    if( alen != blen )
1855        return alen > blen ? -1 : 1;
1856
1857    return vstrcmp( a, b );
1858}
1859
1860static void
1861addDirtyFile( const char  * root,
1862              const char  * filename,
1863              tr_ptrArray * dirtyFolders )
1864{
1865    char * dir = tr_dirname( filename );
1866
1867    /* add the parent folders to dirtyFolders until we reach the root or a known-dirty */
1868    while (     ( dir != NULL )
1869             && ( strlen( root ) <= strlen( dir ) )
1870             && ( tr_ptrArrayFindSorted( dirtyFolders, dir, vstrcmp ) == NULL ) )
1871    {
1872        char * tmp;
1873        tr_ptrArrayInsertSorted( dirtyFolders, tr_strdup( dir ), vstrcmp );
1874
1875        tmp = tr_dirname( dir );
1876        tr_free( dir );
1877        dir = tmp;
1878    }
1879
1880    tr_free( dir );
1881}
1882
1883static void
1884walkLocalData( const tr_torrent * tor,
1885               const char       * root,
1886               const char       * dir,
1887               const char       * base,
1888               tr_ptrArray      * torrentFiles,
1889               tr_ptrArray      * folders,
1890               tr_ptrArray      * dirtyFolders )
1891{
1892    int i;
1893    struct stat sb;
1894    char * buf;
1895
1896    assert( tr_isTorrent( tor ) );
1897
1898    buf = tr_buildPath( dir, base, NULL );
1899    i = stat( buf, &sb );
1900    if( !i )
1901    {
1902        DIR * odir = NULL;
1903
1904        if( S_ISDIR( sb.st_mode ) && ( ( odir = opendir ( buf ) ) ) )
1905        {
1906            struct dirent *d;
1907            tr_ptrArrayInsertSorted( folders, tr_strdup( buf ), vstrcmp );
1908            for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
1909                if( d->d_name && d->d_name[0] != '.' ) /* skip dotfiles */
1910                    walkLocalData( tor, root, buf, d->d_name, torrentFiles, folders, dirtyFolders );
1911            closedir( odir );
1912        }
1913        else if( S_ISREG( sb.st_mode ) && ( sb.st_size > 0 ) )
1914        {
1915            const char * sub = buf + strlen( tor->downloadDir ) + strlen( TR_PATH_DELIMITER_STR );
1916            const tr_bool isTorrentFile = tr_ptrArrayFindSorted( torrentFiles, sub, vstrcmp ) != NULL;
1917            if( !isTorrentFile )
1918                addDirtyFile( root, buf, dirtyFolders );
1919        }
1920    }
1921
1922    tr_free( buf );
1923}
1924
1925static void
1926deleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
1927{
1928    int i, n;
1929    char ** s;
1930    tr_file_index_t f;
1931    tr_ptrArray torrentFiles = TR_PTR_ARRAY_INIT;
1932    tr_ptrArray folders      = TR_PTR_ARRAY_INIT;
1933    tr_ptrArray dirtyFolders = TR_PTR_ARRAY_INIT; /* dirty == contains non-torrent files */
1934
1935    const char * firstFile = tor->info.files[0].name;
1936    const char * cpch = strchr( firstFile, TR_PATH_DELIMITER );
1937    char * tmp = cpch ? tr_strndup( firstFile, cpch - firstFile ) : NULL;
1938    char * root = tr_buildPath( tor->downloadDir, tmp, NULL );
1939
1940    assert( tr_isTorrent( tor ) );
1941
1942    for( f=0; f<tor->info.fileCount; ++f )
1943        tr_ptrArrayInsertSorted( &torrentFiles, tor->info.files[f].name, vstrcmp );
1944
1945    /* build the set of folders and dirtyFolders */
1946    walkLocalData( tor, root, root, NULL, &torrentFiles, &folders, &dirtyFolders );
1947
1948    /* try to remove entire folders first, so that the recycle bin will be tidy */
1949    s = (char**) tr_ptrArrayPeek( &folders, &n );
1950    for( i=0; i<n; ++i )
1951        if( tr_ptrArrayFindSorted( &dirtyFolders, s[i], vstrcmp ) == NULL )
1952            fileFunc( s[i] );
1953
1954    /* now blow away any remaining torrent files, such as torrent files in dirty folders */
1955    for( f=0; f<tor->info.fileCount; ++f ) {
1956        char * path = tr_buildPath( tor->downloadDir, tor->info.files[f].name, NULL );
1957        fileFunc( path );
1958        tr_free( path );
1959    }
1960
1961    /* Now clean out the directories left empty from the previous step.
1962     * Work from deepest to shallowest s.t. lower folders
1963     * won't prevent the upper folders from being deleted */
1964    {
1965        tr_ptrArray cleanFolders = TR_PTR_ARRAY_INIT;
1966        s = (char**) tr_ptrArrayPeek( &folders, &n );
1967        for( i=0; i<n; ++i )
1968            if( tr_ptrArrayFindSorted( &dirtyFolders, s[i], vstrcmp ) == NULL )
1969                tr_ptrArrayInsertSorted( &cleanFolders, s[i], compareLongestFirst );
1970        s = (char**) tr_ptrArrayPeek( &cleanFolders, &n );
1971        for( i=0; i<n; ++i )
1972            fileFunc( s[i] );
1973        tr_ptrArrayDestruct( &cleanFolders, NULL );
1974    }
1975
1976    /* cleanup */
1977    tr_ptrArrayDestruct( &dirtyFolders, tr_free );
1978    tr_ptrArrayDestruct( &folders, tr_free );
1979    tr_ptrArrayDestruct( &torrentFiles, NULL );
1980    tr_free( root );
1981    tr_free( tmp );
1982}
1983
1984void
1985tr_torrentDeleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
1986{
1987    assert( tr_isTorrent( tor ) );
1988
1989    if( fileFunc == NULL )
1990        fileFunc = remove;
1991
1992    /* close all the files because we're about to delete them */
1993    tr_torrentCloseLocalFiles( tor );
1994
1995    if( tor->info.fileCount > 1 )
1996        deleteLocalData( tor, fileFunc );
1997    else {
1998        /* torrent only has one file */
1999        char * path = tr_buildPath( tor->downloadDir, tor->info.files[0].name, NULL );
2000        fileFunc( path );
2001        tr_free( path );
2002    }
2003}
Note: See TracBrowser for help on using the repository browser.