source: trunk/libtransmission/torrent.c @ 9403

Last change on this file since 9403 was 9403, checked in by livings124, 13 years ago

since the Mac UI is the only one to use it, move percentRatio calculation into the Mac code

  • Property svn:keywords set to Date Rev Author Id
File size: 69.1 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 9403 2009-10-25 18:45:35Z livings124 $
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 <math.h> /* fabs */
21#include <stdarg.h>
22#include <string.h> /* memcmp */
23#include <stdlib.h> /* qsort */
24
25#include <event.h> /* evbuffer */
26
27#include "transmission.h"
28#include "announcer.h"
29#include "bandwidth.h"
30#include "bencode.h"
31#include "completion.h"
32#include "crypto.h" /* for tr_sha1 */
33#include "resume.h"
34#include "fdlimit.h" /* tr_fdTorrentClose */
35#include "metainfo.h"
36#include "peer-mgr.h"
37#include "platform.h" /* TR_PATH_DELIMITER_STR */
38#include "ptrarray.h"
39#include "ratecontrol.h"
40#include "session.h"
41#include "torrent.h"
42#include "trevent.h"
43#include "utils.h"
44#include "verify.h"
45
46#define MAX_BLOCK_SIZE ( 1024 * 16 )
47
48/***
49****
50***/
51
52int
53tr_torrentId( const tr_torrent * tor )
54{
55    return tor->uniqueId;
56}
57
58tr_torrent*
59tr_torrentFindFromId( tr_session * session, int id )
60{
61    tr_torrent * tor = NULL;
62
63    while(( tor = tr_torrentNext( session, tor )))
64        if( tor->uniqueId == id )
65            return tor;
66
67    return NULL;
68}
69
70tr_torrent*
71tr_torrentFindFromHashString( tr_session *  session, const char * str )
72{
73    tr_torrent * tor = NULL;
74
75    while(( tor = tr_torrentNext( session, tor )))
76        if( !strcmp( str, tor->info.hashString ) )
77            return tor;
78
79    return NULL;
80}
81
82tr_torrent*
83tr_torrentFindFromHash( tr_session * session, const uint8_t * torrentHash )
84{
85    tr_torrent * tor = NULL;
86
87    while(( tor = tr_torrentNext( session, tor )))
88        if( *tor->info.hash == *torrentHash )
89            if( !memcmp( tor->info.hash, torrentHash, SHA_DIGEST_LENGTH ) )
90                return tor;
91
92    return NULL;
93}
94
95tr_torrent*
96tr_torrentFindFromObfuscatedHash( tr_session * session,
97                                  const uint8_t * obfuscatedTorrentHash )
98{
99    tr_torrent * tor = NULL;
100
101    while(( tor = tr_torrentNext( session, tor )))
102        if( !memcmp( tor->obfuscatedHash, obfuscatedTorrentHash,
103                     SHA_DIGEST_LENGTH ) )
104            return tor;
105
106    return NULL;
107}
108
109/***
110****  PER-TORRENT UL / DL SPEEDS
111***/
112
113void
114tr_torrentSetSpeedLimit( tr_torrent * tor, tr_direction dir, int KiB_sec )
115{
116    assert( tr_isTorrent( tor ) );
117    assert( tr_isDirection( dir ) );
118
119    if( tr_bandwidthSetDesiredSpeed( tor->bandwidth, dir, KiB_sec ) )
120        tr_torrentSetDirty( tor );
121}
122
123int
124tr_torrentGetSpeedLimit( const tr_torrent * tor, tr_direction dir )
125{
126    assert( tr_isTorrent( tor ) );
127    assert( tr_isDirection( dir ) );
128
129    return tr_bandwidthGetDesiredSpeed( tor->bandwidth, dir );
130}
131
132void
133tr_torrentUseSpeedLimit( tr_torrent * tor, tr_direction dir, tr_bool do_use )
134{
135    assert( tr_isTorrent( tor ) );
136    assert( tr_isDirection( dir ) );
137
138    if( tr_bandwidthSetLimited( tor->bandwidth, dir, do_use ) )
139        tr_torrentSetDirty( tor );
140}
141
142tr_bool
143tr_torrentUsesSpeedLimit( const tr_torrent * tor, tr_direction dir )
144{
145    assert( tr_isTorrent( tor ) );
146    assert( tr_isDirection( dir ) );
147
148    return tr_bandwidthIsLimited( tor->bandwidth, dir );
149}
150
151void
152tr_torrentUseSessionLimits( tr_torrent * tor, tr_bool doUse )
153{
154    tr_bool changed;
155
156    assert( tr_isTorrent( tor ) );
157
158    changed = tr_bandwidthHonorParentLimits( tor->bandwidth, TR_UP, doUse );
159    changed |= tr_bandwidthHonorParentLimits( tor->bandwidth, TR_DOWN, doUse );
160
161    if( changed )
162        tr_torrentSetDirty( tor );
163}
164
165tr_bool
166tr_torrentUsesSessionLimits( const tr_torrent * tor )
167{
168    assert( tr_isTorrent( tor ) );
169
170    return tr_bandwidthAreParentLimitsHonored( tor->bandwidth, TR_UP );
171}
172
173/***
174****
175***/
176
177void
178tr_torrentSetRatioMode( tr_torrent *  tor, tr_ratiolimit mode )
179{
180    assert( tr_isTorrent( tor ) );
181    assert( mode==TR_RATIOLIMIT_GLOBAL || mode==TR_RATIOLIMIT_SINGLE || mode==TR_RATIOLIMIT_UNLIMITED  );
182
183    if( mode != tor->ratioLimitMode )
184    {
185        tor->ratioLimitMode = mode;
186        tor->needsSeedRatioCheck = TRUE;
187
188        tr_torrentSetDirty( tor );
189    }
190}
191
192tr_ratiolimit
193tr_torrentGetRatioMode( const tr_torrent * tor )
194{
195    assert( tr_isTorrent( tor ) );
196
197    return tor->ratioLimitMode;
198}
199
200void
201tr_torrentSetRatioLimit( tr_torrent * tor, double desiredRatio )
202{
203    assert( tr_isTorrent( tor ) );
204
205    if( (int)(desiredRatio*100.0) != (int)(tor->desiredRatio*100.0) )
206    {
207        tor->desiredRatio = desiredRatio;
208
209        tor->needsSeedRatioCheck = TRUE;
210
211        tr_torrentSetDirty( tor );
212    }
213}
214
215double
216tr_torrentGetRatioLimit( const tr_torrent * tor )
217{
218    assert( tr_isTorrent( tor ) );
219
220    return tor->desiredRatio;
221}
222
223tr_bool
224tr_torrentIsPieceTransferAllowed( const tr_torrent  * tor,
225                                  tr_direction        direction )
226{
227    int limit;
228    tr_bool allowed = TRUE;
229
230    if( tr_torrentUsesSpeedLimit( tor, direction ) )
231        if( tr_torrentGetSpeedLimit( tor, direction ) <= 0 )
232            allowed = FALSE;
233
234    if( tr_torrentUsesSessionLimits( tor ) )
235        if( tr_sessionGetActiveSpeedLimit( tor->session, direction, &limit ) )
236            if( limit <= 0 )
237                allowed = FALSE;
238
239    return allowed;
240}
241
242tr_bool
243tr_torrentGetSeedRatio( const tr_torrent * tor, double * ratio )
244{
245    tr_bool isLimited;
246
247    switch( tr_torrentGetRatioMode( tor ) )
248    {
249        case TR_RATIOLIMIT_SINGLE:
250            isLimited = TRUE;
251            if( ratio )
252                *ratio = tr_torrentGetRatioLimit( tor );
253            break;
254
255        case TR_RATIOLIMIT_GLOBAL:
256            isLimited = tr_sessionIsRatioLimited( tor->session );
257            if( isLimited && ratio )
258                *ratio = tr_sessionGetRatioLimit( tor->session );
259            break;
260
261        default: /* TR_RATIOLIMIT_UNLIMITED */
262            isLimited = FALSE;
263            break;
264    }
265
266    return isLimited;
267}
268
269/***
270****
271***/
272
273void
274tr_torrentSetLocalError( tr_torrent * tor, const char * fmt, ... )
275{
276    va_list ap;
277
278    assert( tr_isTorrent( tor ) );
279
280    va_start( ap, fmt );
281    tor->error = TR_STAT_LOCAL_ERROR;
282    evutil_vsnprintf( tor->errorString, sizeof( tor->errorString ), fmt, ap );
283    va_end( ap );
284}
285
286static void
287onTrackerResponse( void * tracker UNUSED,
288                   void * vevent,
289                   void * user_data )
290{
291    tr_torrent *       tor = user_data;
292    tr_tracker_event * event = vevent;
293
294    switch( event->messageType )
295    {
296        case TR_TRACKER_PEERS:
297        {
298            size_t   i, n;
299            tr_pex * pex = tr_peerMgrArrayToPex( event->compact,
300                                                 event->compactLen, &n );
301             if( event->allAreSeeds )
302                tr_tordbg( tor, "Got %d seeds from tracker", (int)n );
303            else
304                tr_torinf( tor, _( "Got %d peers from tracker" ), (int)n );
305
306            for( i = 0; i < n; ++i )
307            {
308                if( event->allAreSeeds )
309                    pex[i].flags |= ADDED_F_SEED_FLAG;
310                tr_peerMgrAddPex( tor, TR_PEER_FROM_TRACKER, pex + i );
311            }
312
313            tr_free( pex );
314            break;
315        }
316
317        case TR_TRACKER_WARNING:
318            tr_torerr( tor, _( "Tracker warning: \"%s\"" ), event->text );
319            tor->error = TR_STAT_TRACKER_WARNING;
320            tr_strlcpy( tor->errorString, event->text, sizeof( tor->errorString ) );
321            break;
322
323        case TR_TRACKER_ERROR:
324            tr_torerr( tor, _( "Tracker error: \"%s\"" ), event->text );
325            tor->error = TR_STAT_TRACKER_ERROR;
326            tr_strlcpy( tor->errorString, event->text, sizeof( tor->errorString ) );
327            break;
328
329        case TR_TRACKER_ERROR_CLEAR:
330            tor->error = TR_STAT_OK;
331            tor->errorString[0] = '\0';
332            break;
333    }
334}
335
336/***
337****
338****  TORRENT INSTANTIATION
339****
340***/
341
342static int
343getBytePiece( const tr_info * info,
344              uint64_t        byteOffset )
345{
346    assert( info );
347    assert( info->pieceSize != 0 );
348
349    return byteOffset / info->pieceSize;
350}
351
352static void
353initFilePieces( tr_info *       info,
354                tr_file_index_t fileIndex )
355{
356    tr_file * file;
357    uint64_t  firstByte, lastByte;
358
359    assert( info );
360    assert( fileIndex < info->fileCount );
361
362    file = &info->files[fileIndex];
363    firstByte = file->offset;
364    lastByte = firstByte + ( file->length ? file->length - 1 : 0 );
365    file->firstPiece = getBytePiece( info, firstByte );
366    file->lastPiece = getBytePiece( info, lastByte );
367}
368
369static int
370pieceHasFile( tr_piece_index_t piece,
371              const tr_file *  file )
372{
373    return ( file->firstPiece <= piece ) && ( piece <= file->lastPiece );
374}
375
376static tr_priority_t
377calculatePiecePriority( const tr_torrent * tor,
378                        tr_piece_index_t   piece,
379                        int                fileHint )
380{
381    tr_file_index_t i;
382    int             priority = TR_PRI_LOW;
383
384    /* find the first file that has data in this piece */
385    if( fileHint >= 0 ) {
386        i = fileHint;
387        while( i > 0 && pieceHasFile( piece, &tor->info.files[i - 1] ) )
388            --i;
389    } else {
390        for( i = 0; i < tor->info.fileCount; ++i )
391            if( pieceHasFile( piece, &tor->info.files[i] ) )
392                break;
393    }
394
395    /* the piece's priority is the max of the priorities
396     * of all the files in that piece */
397    for( ; i < tor->info.fileCount; ++i )
398    {
399        const tr_file * file = &tor->info.files[i];
400
401        if( !pieceHasFile( piece, file ) )
402            break;
403
404        priority = MAX( priority, file->priority );
405
406        /* when dealing with multimedia files, getting the first and
407           last pieces can sometimes allow you to preview it a bit
408           before it's fully downloaded... */
409        if( file->priority >= TR_PRI_NORMAL )
410            if( file->firstPiece == piece || file->lastPiece == piece )
411                priority = TR_PRI_HIGH;
412    }
413
414    return priority;
415}
416
417static void
418tr_torrentInitFilePieces( tr_torrent * tor )
419{
420    tr_file_index_t  f;
421    tr_piece_index_t p;
422    uint64_t offset = 0;
423    tr_info * inf = &tor->info;
424    int * firstFiles;
425
426    /* assign the file offsets */
427    for( f=0; f<inf->fileCount; ++f ) {
428        inf->files[f].offset = offset;
429        offset += inf->files[f].length;
430        initFilePieces( inf, f );
431    }
432
433    /* build the array of first-file hints to give calculatePiecePriority */
434    firstFiles = tr_new( int, inf->pieceCount );
435    for( p=f=0; p<inf->pieceCount; ++p ) {
436        while( inf->files[f].lastPiece < p )
437            ++f;
438        firstFiles[p] = f;
439    }
440
441#if 0
442    /* test to confirm the first-file hints are correct */
443    for( p=0; p<inf->pieceCount; ++p ) {
444        f = firstFiles[p];
445        assert( inf->files[f].firstPiece <= p );
446        assert( inf->files[f].lastPiece >= p );
447        if( f > 0 )
448            assert( inf->files[f-1].lastPiece < p );
449        for( f=0; f<inf->fileCount; ++f )
450            if( pieceHasFile( p, &inf->files[f] ) )
451                break;
452        assert( (int)f == firstFiles[p] );
453    }
454#endif
455
456    for( p=0; p<inf->pieceCount; ++p )
457        inf->pieces[p].priority = calculatePiecePriority( tor, p, firstFiles[p] );
458
459    tr_free( firstFiles );
460}
461
462int
463tr_torrentPromoteTracker( tr_torrent * tor,
464                          int          pos )
465{
466    int i;
467    int tier;
468
469    assert( tor );
470    assert( ( 0 <= pos ) && ( pos < tor->info.trackerCount ) );
471
472    /* the tier of the tracker we're promoting */
473    tier = tor->info.trackers[pos].tier;
474
475    /* find the index of that tier's first tracker */
476    for( i = 0; i < tor->info.trackerCount; ++i )
477        if( tor->info.trackers[i].tier == tier )
478            break;
479
480    assert( i < tor->info.trackerCount );
481
482    /* promote the tracker at `pos' to the front of the tier */
483    if( i != pos )
484    {
485        const tr_tracker_info tmp = tor->info.trackers[i];
486        tor->info.trackers[i] = tor->info.trackers[pos];
487        tor->info.trackers[pos] = tmp;
488    }
489
490    /* return the new position of the tracker that started out at [pos] */
491    return i;
492}
493
494struct RandomTracker
495{
496    tr_tracker_info    tracker;
497    int                random_value;
498};
499
500/* the tiers will be sorted from lowest to highest,
501 * and trackers are randomized within the tiers */
502static TR_INLINE int
503compareRandomTracker( const void * va,
504                      const void * vb )
505{
506    const struct RandomTracker * a = va;
507    const struct RandomTracker * b = vb;
508
509    if( a->tracker.tier != b->tracker.tier )
510        return a->tracker.tier - b->tracker.tier;
511
512    return a->random_value - b->random_value;
513}
514
515static void
516randomizeTiers( tr_info * info )
517{
518    int                    i;
519    const int              n = info->trackerCount;
520    struct RandomTracker * r = tr_new0( struct RandomTracker, n );
521
522    for( i = 0; i < n; ++i )
523    {
524        r[i].tracker = info->trackers[i];
525        r[i].random_value = tr_cryptoRandInt( INT_MAX );
526    }
527    qsort( r, n, sizeof( struct RandomTracker ), compareRandomTracker );
528    for( i = 0; i < n; ++i )
529        info->trackers[i] = r[i].tracker;
530    tr_free( r );
531}
532
533static void torrentStart( tr_torrent * tor );
534
535/**
536 * Decide on a block size.  constraints:
537 * (1) most clients decline requests over 16 KiB
538 * (2) pieceSize must be a multiple of block size
539 */
540static uint32_t
541getBlockSize( uint32_t pieceSize )
542{
543    uint32_t b = pieceSize;
544
545    while( b > MAX_BLOCK_SIZE )
546        b /= 2u;
547
548    if( !b || ( pieceSize % b ) ) /* not cleanly divisible */
549        return 0;
550    return b;
551}
552
553static void refreshCurrentDir( tr_torrent * tor );;
554
555static void
556torrentRealInit( tr_torrent * tor, const tr_ctor * ctor )
557{
558    int          doStart;
559    uint64_t     loaded;
560    uint64_t     t;
561    const char * dir;
562    static int   nextUniqueId = 1;
563    tr_info    * info = &tor->info;
564    tr_session * session = tr_ctorGetSession( ctor );
565
566    assert( session != NULL );
567
568    tr_globalLock( session );
569
570    tor->session   = session;
571    tor->uniqueId = nextUniqueId++;
572    tor->magicNumber = TORRENT_MAGIC_NUMBER;
573
574    randomizeTiers( info );
575
576    tor->bandwidth = tr_bandwidthNew( session, session->bandwidth );
577
578    tor->blockSize = getBlockSize( info->pieceSize );
579
580    if( !tr_ctorGetDownloadDir( ctor, TR_FORCE, &dir ) ||
581        !tr_ctorGetDownloadDir( ctor, TR_FALLBACK, &dir ) )
582            tor->downloadDir = tr_strdup( dir );
583
584    if( tr_ctorGetIncompleteDir( ctor, &dir ) )
585        dir = tr_sessionGetIncompleteDir( session );
586    if( tr_sessionIsIncompleteDirEnabled( session ) )
587        tor->incompleteDir = tr_strdup( dir );
588
589    tor->lastPieceSize = info->totalSize % info->pieceSize;
590
591    if( !tor->lastPieceSize )
592        tor->lastPieceSize = info->pieceSize;
593
594    tor->lastBlockSize = info->totalSize % tor->blockSize;
595
596    if( !tor->lastBlockSize )
597        tor->lastBlockSize = tor->blockSize;
598
599    tor->blockCount =
600        ( info->totalSize + tor->blockSize - 1 ) / tor->blockSize;
601
602    tor->blockCountInPiece =
603        info->pieceSize / tor->blockSize;
604
605    tor->blockCountInLastPiece =
606        ( tor->lastPieceSize + tor->blockSize - 1 ) / tor->blockSize;
607
608    /* check our work */
609    assert( ( info->pieceSize % tor->blockSize ) == 0 );
610    t = info->pieceCount - 1;
611    t *= info->pieceSize;
612    t += tor->lastPieceSize;
613    assert( t == info->totalSize );
614    t = tor->blockCount - 1;
615    t *= tor->blockSize;
616    t += tor->lastBlockSize;
617    assert( t == info->totalSize );
618    t = info->pieceCount - 1;
619    t *= tor->blockCountInPiece;
620    t += tor->blockCountInLastPiece;
621    assert( t == (uint64_t)tor->blockCount );
622
623    tr_cpConstruct( &tor->completion, tor );
624
625    tr_torrentInitFilePieces( tor );
626
627    tr_rcConstruct( &tor->swarmSpeed );
628
629    tr_sha1( tor->obfuscatedHash, "req2", 4,
630             info->hash, SHA_DIGEST_LENGTH,
631             NULL );
632
633    tr_peerMgrAddTorrent( session->peerMgr, tor );
634
635    assert( !tor->downloadedCur );
636    assert( !tor->uploadedCur );
637
638    tr_ctorInitTorrentPriorities( ctor, tor );
639
640    tr_ctorInitTorrentWanted( ctor, tor );
641
642    tor->error = TR_STAT_OK;
643
644    tr_bitfieldConstruct( &tor->checkedPieces, tor->info.pieceCount );
645    tr_torrentUncheck( tor );
646
647    tr_torrentSetAddedDate( tor, time( NULL ) ); /* this is a default value to be
648                                                    overwritten by the resume file */
649
650    loaded = tr_torrentLoadResume( tor, ~0, ctor );
651
652    refreshCurrentDir( tor );
653
654    doStart = tor->isRunning;
655    tor->isRunning = 0;
656
657    if( !( loaded & TR_FR_SPEEDLIMIT ) )
658    {
659        tr_torrentUseSpeedLimit( tor, TR_UP, FALSE );
660        tr_torrentSetSpeedLimit( tor, TR_UP, tr_sessionGetSpeedLimit( tor->session, TR_UP ) );
661        tr_torrentUseSpeedLimit( tor, TR_DOWN, FALSE );
662        tr_torrentSetSpeedLimit( tor, TR_DOWN, tr_sessionGetSpeedLimit( tor->session, TR_DOWN ) );
663        tr_torrentUseSessionLimits( tor, TRUE );
664    }
665
666    if( !( loaded & TR_FR_RATIOLIMIT ) )
667    {
668        tr_torrentSetRatioMode( tor, TR_RATIOLIMIT_GLOBAL );
669        tr_torrentSetRatioLimit( tor, tr_sessionGetRatioLimit( tor->session ) );
670    }
671
672    tor->completeness = tr_cpGetStatus( &tor->completion );
673
674    {
675        tr_torrent * it = NULL;
676        tr_torrent * last = NULL;
677        while( ( it = tr_torrentNext( session, it ) ) )
678            last = it;
679
680        if( !last )
681            session->torrentList = tor;
682        else
683            last->next = tor;
684        ++session->torrentCount;
685    }
686
687    tr_globalUnlock( session );
688
689    /* maybe save our own copy of the metainfo */
690    if( tr_ctorGetSave( ctor ) )
691    {
692        const tr_benc * val;
693        if( !tr_ctorGetMetainfo( ctor, &val ) )
694        {
695            const char * filename = tor->info.torrent;
696            tr_bencToFile( val, TR_FMT_BENC, filename );
697            tr_sessionSetTorrentFile( tor->session, tor->info.hashString, filename );
698        }
699    }
700
701    tor->tiers = tr_announcerAddTorrent( session->announcer, tor );
702    tor->tiersSubscription = tr_announcerSubscribe( tor->tiers, onTrackerResponse, tor );
703
704    tr_metainfoMigrate( session, &tor->info );
705
706    if( doStart )
707        torrentStart( tor );
708}
709
710tr_parse_result
711tr_torrentParse( const tr_ctor * ctor, tr_info * setmeInfo )
712{
713    int             doFree;
714    tr_bool         didParse;
715    tr_info         tmp;
716    const tr_benc * metainfo;
717    tr_session    * session = tr_ctorGetSession( ctor );
718    tr_parse_result result = TR_PARSE_OK;
719
720    if( setmeInfo == NULL )
721        setmeInfo = &tmp;
722    memset( setmeInfo, 0, sizeof( tr_info ) );
723
724    if( tr_ctorGetMetainfo( ctor, &metainfo ) )
725        return TR_PARSE_ERR;
726
727    didParse = tr_metainfoParse( session, setmeInfo, metainfo );
728    doFree = didParse && ( setmeInfo == &tmp );
729
730    if( !didParse )
731        result = TR_PARSE_ERR;
732
733    if( didParse && !getBlockSize( setmeInfo->pieceSize ) )
734        result = TR_PARSE_ERR;
735
736    if( didParse && session && tr_torrentExists( session, setmeInfo->hash ) )
737        result = TR_PARSE_DUPLICATE;
738
739    if( doFree )
740        tr_metainfoFree( setmeInfo );
741
742    return result;
743}
744
745tr_torrent *
746tr_torrentNew( const tr_ctor  * ctor,
747               int            * setmeError )
748{
749    int          err;
750    tr_info      tmpInfo;
751    tr_torrent * tor = NULL;
752
753    assert( ctor != NULL );
754    assert( tr_isSession( tr_ctorGetSession( ctor ) ) );
755
756    err = tr_torrentParse( ctor, &tmpInfo );
757    if( !err )
758    {
759        tor = tr_new0( tr_torrent, 1 );
760        tor->info = tmpInfo;
761        torrentRealInit( tor, ctor );
762    }
763    else if( setmeError )
764    {
765        *setmeError = err;
766    }
767
768    return tor;
769}
770
771/**
772***
773**/
774
775void
776tr_torrentSetDownloadDir( tr_torrent * tor, const char * path )
777{
778    assert( tr_isTorrent( tor  ) );
779
780    if( !path || !tor->downloadDir || strcmp( path, tor->downloadDir ) )
781    {
782        tr_free( tor->downloadDir );
783        tor->downloadDir = tr_strdup( path );
784        tr_torrentSetDirty( tor );
785    }
786
787    refreshCurrentDir( tor );
788}
789
790const char*
791tr_torrentGetDownloadDir( const tr_torrent * tor )
792{
793    assert( tr_isTorrent( tor  ) );
794
795    return tor->downloadDir;
796}
797
798const char *
799tr_torrentGetCurrentDir( const tr_torrent * tor )
800{
801    assert( tr_isTorrent( tor  ) );
802
803    return tor->currentDir;
804}
805
806
807void
808tr_torrentChangeMyPort( tr_torrent * tor )
809{
810    assert( tr_isTorrent( tor  ) );
811
812    tr_announcerChangeMyPort( tor );
813}
814
815static TR_INLINE void
816tr_torrentManualUpdateImpl( void * vtor )
817{
818    tr_torrent * tor = vtor;
819
820    assert( tr_isTorrent( tor  ) );
821
822    if( tor->isRunning )
823        tr_announcerManualAnnounce( tor );
824}
825
826void
827tr_torrentManualUpdate( tr_torrent * tor )
828{
829    assert( tr_isTorrent( tor  ) );
830
831    tr_runInEventThread( tor->session, tr_torrentManualUpdateImpl, tor );
832}
833
834tr_bool
835tr_torrentCanManualUpdate( const tr_torrent * tor )
836{
837    return ( tr_isTorrent( tor  ) )
838        && ( tor->isRunning )
839        && ( tr_announcerCanManualAnnounce( tor ) );
840}
841
842const tr_info *
843tr_torrentInfo( const tr_torrent * tor )
844{
845    return tr_isTorrent( tor ) ? &tor->info : NULL;
846}
847
848const tr_stat *
849tr_torrentStatCached( tr_torrent * tor )
850{
851    const time_t now = time( NULL );
852
853    return tr_isTorrent( tor ) && ( now == tor->lastStatTime )
854         ? &tor->stats
855         : tr_torrentStat( tor );
856}
857
858void
859tr_torrentSetVerifyState( tr_torrent * tor, tr_verify_state state )
860{
861    assert( tr_isTorrent( tor ) );
862    assert( state==TR_VERIFY_NONE || state==TR_VERIFY_WAIT || state==TR_VERIFY_NOW );
863
864    tor->verifyState = state;
865    tor->anyDate = time( NULL );
866}
867
868tr_torrent_activity
869tr_torrentGetActivity( tr_torrent * tor )
870{
871    assert( tr_isTorrent( tor ) );
872
873    tr_torrentRecheckCompleteness( tor );
874
875    if( tor->verifyState == TR_VERIFY_NOW )
876        return TR_STATUS_CHECK;
877    if( tor->verifyState == TR_VERIFY_WAIT )
878        return TR_STATUS_CHECK_WAIT;
879    if( !tor->isRunning )
880        return TR_STATUS_STOPPED;
881    if( tor->completeness == TR_LEECH )
882        return TR_STATUS_DOWNLOAD;
883
884    return TR_STATUS_SEED;
885}
886
887const tr_stat *
888tr_torrentStat( tr_torrent * tor )
889{
890    tr_stat *               s;
891    int                     usableSeeds;
892    uint64_t                now;
893    double                  downloadedForRatio, seedRatio=0;
894    double                  d;
895    tr_bool                 checkSeedRatio;
896
897    if( !tor )
898        return NULL;
899
900    assert( tr_isTorrent( tor ) );
901    tr_torrentLock( tor );
902
903    tor->lastStatTime = time( NULL );
904
905    s = &tor->stats;
906    s->id = tor->uniqueId;
907    s->activity = tr_torrentGetActivity( tor );
908    s->error = tor->error;
909    memcpy( s->errorString, tor->errorString, sizeof( s->errorString ) );
910
911    s->manualAnnounceTime = tr_announcerNextManualAnnounce( tor );
912
913    tr_peerMgrTorrentStats( tor,
914                            &s->peersKnown,
915                            &s->peersConnected,
916                            &usableSeeds,
917                            &s->webseedsSendingToUs,
918                            &s->peersSendingToUs,
919                            &s->peersGettingFromUs,
920                            s->peersFrom );
921
922    now = tr_date( );
923    d = tr_peerMgrGetWebseedSpeed( tor, now );
924    s->swarmSpeed         = tr_rcRate( &tor->swarmSpeed, now );
925    s->rawUploadSpeed     = tr_bandwidthGetRawSpeed  ( tor->bandwidth, now, TR_UP );
926    s->pieceUploadSpeed   = tr_bandwidthGetPieceSpeed( tor->bandwidth, now, TR_UP );
927    s->rawDownloadSpeed   = d + tr_bandwidthGetRawSpeed  ( tor->bandwidth, now, TR_DOWN );
928    s->pieceDownloadSpeed = d + tr_bandwidthGetPieceSpeed( tor->bandwidth, now, TR_DOWN );
929
930    usableSeeds += tor->info.webseedCount;
931
932    s->percentComplete = tr_cpPercentComplete ( &tor->completion );
933
934    s->percentDone   = tr_cpPercentDone  ( &tor->completion );
935    s->leftUntilDone = tr_cpLeftUntilDone( &tor->completion );
936    s->sizeWhenDone  = tr_cpSizeWhenDone ( &tor->completion );
937
938    s->recheckProgress = s->activity == TR_STATUS_CHECK
939                       ? 1.0 -
940                         ( tr_torrentCountUncheckedPieces( tor ) /
941                           (double) tor->info.pieceCount )
942                       : 0.0;
943
944    s->activityDate = tor->activityDate;
945    s->addedDate    = tor->addedDate;
946    s->doneDate     = tor->doneDate;
947    s->startDate    = tor->startDate;
948
949    s->corruptEver     = tor->corruptCur    + tor->corruptPrev;
950    s->downloadedEver  = tor->downloadedCur + tor->downloadedPrev;
951    s->uploadedEver    = tor->uploadedCur   + tor->uploadedPrev;
952    s->haveValid       = tr_cpHaveValid( &tor->completion );
953    s->haveUnchecked   = tr_cpHaveTotal( &tor->completion ) - s->haveValid;
954
955    if( usableSeeds > 0 )
956    {
957        s->desiredAvailable = s->leftUntilDone;
958    }
959    else if( !s->leftUntilDone || !s->peersConnected )
960    {
961        s->desiredAvailable = 0;
962    }
963    else
964    {
965        tr_piece_index_t i;
966        tr_bitfield *    peerPieces = tr_peerMgrGetAvailable( tor );
967        s->desiredAvailable = 0;
968        for( i = 0; i < tor->info.pieceCount; ++i )
969            if( !tor->info.pieces[i].dnd && tr_bitfieldHasFast( peerPieces, i ) )
970                s->desiredAvailable += tr_cpMissingBlocksInPiece( &tor->completion, i );
971        s->desiredAvailable *= tor->blockSize;
972        tr_bitfieldFree( peerPieces );
973    }
974
975    downloadedForRatio = s->downloadedEver ? s->downloadedEver : s->haveValid;
976    s->ratio = tr_getRatio( s->uploadedEver, downloadedForRatio );
977
978    checkSeedRatio = tr_torrentGetSeedRatio( tor, &seedRatio );
979
980    switch( s->activity )
981    {
982        /* etaXLSpeed exists because if we use the piece speed directly,
983         * brief fluctuations cause the ETA to jump all over the place.
984         * so, etaXLSpeed is a smoothed-out version of the piece speed
985         * to dampen the effect of fluctuations */
986
987        case TR_STATUS_DOWNLOAD:
988            if( ( tor->etaDLSpeedCalculatedAt + 800 ) < now ) {
989                tor->etaDLSpeed = ( ( tor->etaDLSpeedCalculatedAt + 4000 ) < now )
990                    ? s->pieceDownloadSpeed /* if no recent previous speed, no need to smooth */
991                    : 0.8*tor->etaDLSpeed + 0.2*s->pieceDownloadSpeed; /* smooth across 5 readings */
992                tor->etaDLSpeedCalculatedAt = now;
993            }
994           
995            if( s->leftUntilDone > s->desiredAvailable )
996                s->eta = TR_ETA_NOT_AVAIL;
997            else if( s->pieceDownloadSpeed < 0.1 )
998                s->eta = TR_ETA_UNKNOWN;
999            else
1000                s->eta = s->leftUntilDone / tor->etaDLSpeed / 1024.0;
1001            break;
1002
1003        case TR_STATUS_SEED:
1004            if( checkSeedRatio )
1005            {
1006                if( ( tor->etaULSpeedCalculatedAt + 800 ) < now ) {
1007                    tor->etaULSpeed = ( ( tor->etaULSpeedCalculatedAt + 4000 ) < now )
1008                        ? s->pieceUploadSpeed /* if no recent previous speed, no need to smooth */
1009                        : 0.8*tor->etaULSpeed + 0.2*s->pieceUploadSpeed; /* smooth across 5 readings */
1010                    tor->etaULSpeedCalculatedAt = now;
1011                }
1012               
1013                if( s->pieceUploadSpeed < 0.1 )
1014                    s->eta = TR_ETA_UNKNOWN;
1015                else
1016                    s->eta = (downloadedForRatio * (seedRatio - s->ratio)) / tor->etaULSpeed / 1024.0;
1017            }
1018            else
1019                s->eta = TR_ETA_NOT_AVAIL;
1020            break;
1021
1022        default:
1023            s->eta = TR_ETA_NOT_AVAIL;
1024            break;
1025    }
1026
1027    tr_torrentUnlock( tor );
1028
1029    return s;
1030}
1031
1032/***
1033****
1034***/
1035
1036static uint64_t
1037fileBytesCompleted( const tr_torrent * tor, tr_file_index_t index )
1038{
1039    uint64_t total = 0;
1040    const tr_file * f = &tor->info.files[index];
1041
1042    if( f->length )
1043    {
1044        const tr_block_index_t firstBlock = f->offset / tor->blockSize;
1045        const uint64_t lastByte = f->offset + f->length - 1;
1046        const tr_block_index_t lastBlock = lastByte / tor->blockSize;
1047
1048        if( firstBlock == lastBlock )
1049        {
1050            if( tr_cpBlockIsCompleteFast( &tor->completion, firstBlock ) )
1051                total = f->length;
1052        }
1053        else
1054        {
1055            uint32_t i;
1056
1057            /* the first block */
1058            if( tr_cpBlockIsCompleteFast( &tor->completion, firstBlock ) )
1059                total += tor->blockSize - ( f->offset % tor->blockSize );
1060
1061            /* the middle blocks */
1062            if( f->firstPiece == f->lastPiece )
1063            {
1064                for( i=firstBlock+1; i<lastBlock; ++i )
1065                    if( tr_cpBlockIsCompleteFast( &tor->completion, i ) )
1066                        total += tor->blockSize;
1067            }
1068            else
1069            {
1070                int64_t b = 0;
1071                const tr_block_index_t firstBlockOfLastPiece
1072                           = tr_torPieceFirstBlock( tor, f->lastPiece );
1073                const tr_block_index_t lastBlockOfFirstPiece
1074                           = tr_torPieceFirstBlock( tor, f->firstPiece )
1075                             + tr_torPieceCountBlocks( tor, f->firstPiece ) - 1;
1076
1077                /* the rest of the first piece */
1078                for( i=firstBlock+1; i<lastBlock && i<=lastBlockOfFirstPiece; ++i )
1079                    if( tr_cpBlockIsCompleteFast( &tor->completion, i ) )
1080                        ++b;
1081
1082                /* the middle pieces */
1083                if( f->firstPiece + 1 < f->lastPiece )
1084                    for( i=f->firstPiece+1; i<f->lastPiece; ++i )
1085                        b += tor->blockCountInPiece - tr_cpMissingBlocksInPiece( &tor->completion, i );
1086
1087                /* the rest of the last piece */
1088                for( i=firstBlockOfLastPiece; i<lastBlock; ++i )
1089                    if( tr_cpBlockIsCompleteFast( &tor->completion, i ) )
1090                        ++b;
1091
1092                b *= tor->blockSize;
1093                total += b;
1094            }
1095
1096            /* the last block */
1097            if( tr_cpBlockIsCompleteFast( &tor->completion, lastBlock ) )
1098                total += ( f->offset + f->length ) - ( tor->blockSize * lastBlock );
1099        }
1100    }
1101
1102    return total;
1103}
1104
1105tr_file_stat *
1106tr_torrentFiles( const tr_torrent * tor,
1107                 tr_file_index_t *  fileCount )
1108{
1109    tr_file_index_t       i;
1110    const tr_file_index_t n = tor->info.fileCount;
1111    tr_file_stat *        files = tr_new0( tr_file_stat, n );
1112    tr_file_stat *        walk = files;
1113    const tr_bool         isSeed = tor->completeness == TR_SEED;
1114
1115    assert( tr_isTorrent( tor ) );
1116
1117    for( i = 0; i < n; ++i, ++walk )
1118    {
1119        const uint64_t b = isSeed ? tor->info.files[i].length : fileBytesCompleted( tor, i );
1120        walk->bytesCompleted = b;
1121        walk->progress = tr_getRatio( b, tor->info.files[i].length );
1122    }
1123
1124    if( fileCount )
1125        *fileCount = n;
1126
1127    return files;
1128}
1129
1130void
1131tr_torrentFilesFree( tr_file_stat *            files,
1132                     tr_file_index_t fileCount UNUSED )
1133{
1134    tr_free( files );
1135}
1136
1137/***
1138****
1139***/
1140
1141float*
1142tr_torrentWebSpeeds( const tr_torrent * tor )
1143{
1144    return tr_isTorrent( tor )
1145         ? tr_peerMgrWebSpeeds( tor )
1146         : NULL;
1147}
1148
1149tr_peer_stat *
1150tr_torrentPeers( const tr_torrent * tor,
1151                 int *              peerCount )
1152{
1153    tr_peer_stat * ret = NULL;
1154
1155    if( tr_isTorrent( tor ) )
1156        ret = tr_peerMgrPeerStats( tor, peerCount );
1157
1158    return ret;
1159}
1160
1161void
1162tr_torrentPeersFree( tr_peer_stat * peers,
1163                     int peerCount  UNUSED )
1164{
1165    tr_free( peers );
1166}
1167
1168tr_tracker_stat *
1169tr_torrentTrackers( const tr_torrent * torrent,
1170                    int              * setmeTrackerCount )
1171{
1172    assert( tr_isTorrent( torrent ) );
1173
1174    return tr_announcerStats( torrent, setmeTrackerCount );
1175}
1176
1177void
1178tr_torrentTrackersFree( tr_tracker_stat * trackers,
1179                        int trackerCount )
1180{
1181    tr_announcerStatsFree( trackers, trackerCount );
1182}
1183
1184void
1185tr_torrentAvailability( const tr_torrent * tor,
1186                        int8_t *           tab,
1187                        int                size )
1188{
1189    tr_peerMgrTorrentAvailability( tor, tab, size );
1190}
1191
1192void
1193tr_torrentAmountFinished( const tr_torrent * tor,
1194                          float *            tab,
1195                          int                size )
1196{
1197    assert( tr_isTorrent( tor ) );
1198
1199    tr_torrentLock( tor );
1200    tr_cpGetAmountDone( &tor->completion, tab, size );
1201    tr_torrentUnlock( tor );
1202}
1203
1204static void
1205tr_torrentResetTransferStats( tr_torrent * tor )
1206{
1207    assert( tr_isTorrent( tor ) );
1208
1209    tr_torrentLock( tor );
1210
1211    tor->downloadedPrev += tor->downloadedCur;
1212    tor->downloadedCur   = 0;
1213    tor->uploadedPrev   += tor->uploadedCur;
1214    tor->uploadedCur     = 0;
1215    tor->corruptPrev    += tor->corruptCur;
1216    tor->corruptCur      = 0;
1217
1218    tr_torrentSetDirty( tor );
1219
1220    tr_torrentUnlock( tor );
1221}
1222
1223void
1224tr_torrentSetHasPiece( tr_torrent *     tor,
1225                       tr_piece_index_t pieceIndex,
1226                       tr_bool          has )
1227{
1228    assert( tr_isTorrent( tor ) );
1229    assert( pieceIndex < tor->info.pieceCount );
1230
1231    if( has )
1232        tr_cpPieceAdd( &tor->completion, pieceIndex );
1233    else
1234        tr_cpPieceRem( &tor->completion, pieceIndex );
1235}
1236
1237/***
1238****
1239***/
1240
1241static void
1242freeTorrent( tr_torrent * tor )
1243{
1244    tr_torrent * t;
1245    tr_session *  session = tor->session;
1246    tr_info *    inf = &tor->info;
1247
1248    assert( tr_isTorrent( tor ) );
1249    assert( !tor->isRunning );
1250
1251    tr_globalLock( session );
1252
1253    tr_peerMgrRemoveTorrent( tor );
1254
1255    tr_cpDestruct( &tor->completion );
1256
1257    tr_rcDestruct( &tor->swarmSpeed );
1258
1259    tr_announcerUnsubscribe( tor->tiers, tor->tiersSubscription );
1260    tr_announcerRemoveTorrent( session->announcer, tor );
1261
1262    tr_bitfieldDestruct( &tor->checkedPieces );
1263
1264    tr_free( tor->downloadDir );
1265    tr_free( tor->incompleteDir );
1266    tr_free( tor->peer_id );
1267
1268    if( tor == session->torrentList )
1269        session->torrentList = tor->next;
1270    else for( t = session->torrentList; t != NULL; t = t->next ) {
1271        if( t->next == tor ) {
1272            t->next = tor->next;
1273            break;
1274        }
1275    }
1276
1277    assert( session->torrentCount >= 1 );
1278    session->torrentCount--;
1279
1280    tr_bandwidthFree( tor->bandwidth );
1281
1282    tr_metainfoFree( inf );
1283    tr_free( tor );
1284
1285    tr_globalUnlock( session );
1286}
1287
1288/**
1289***  Start/Stop Callback
1290**/
1291
1292static void
1293checkAndStartImpl( void * vtor )
1294{
1295    tr_torrent * tor = vtor;
1296
1297    assert( tr_isTorrent( tor ) );
1298
1299    tr_globalLock( tor->session );
1300
1301    /** If we had local data before, but it's disappeared,
1302        stop the torrent and log an error. */
1303    if( tor->preVerifyTotal && !tr_cpHaveTotal( &tor->completion ) )
1304    {
1305        tr_torrentSetLocalError( tor, _( "Can't find local data.  Try \"Set Location\" to find it, or restart the torrent to re-download." ) );
1306        tr_torrentStop( tor );
1307    }
1308    else
1309    {
1310        const time_t now = time( NULL );
1311        tor->isRunning = TRUE;
1312        tor->needsSeedRatioCheck = TRUE;
1313        tor->error = TR_STAT_OK;
1314        tor->errorString[0] = '\0';
1315        tor->completeness = tr_cpGetStatus( &tor->completion );
1316        tor->startDate = tor->anyDate = now;
1317
1318        tr_torrentResetTransferStats( tor );
1319        tr_announcerTorrentStarted( tor );
1320        tor->dhtAnnounceAt = now + tr_cryptoWeakRandInt( 20 );
1321        tr_peerMgrStartTorrent( tor );
1322    }
1323
1324    tr_globalUnlock( tor->session );
1325}
1326
1327static void
1328checkAndStartCB( tr_torrent * tor )
1329{
1330    assert( tr_isTorrent( tor ) );
1331    assert( tr_isSession( tor->session ) );
1332
1333    tr_runInEventThread( tor->session, checkAndStartImpl, tor );
1334}
1335
1336static void
1337torrentStart( tr_torrent * tor )
1338{
1339    assert( tr_isTorrent( tor ) );
1340
1341    tr_globalLock( tor->session );
1342
1343    if( !tor->isRunning )
1344    {
1345        tr_verifyRemove( tor );
1346
1347        /* corresponds to the peer_id sent as a tracker request parameter.
1348         * one tracker admin says: "When the same torrent is opened and
1349         * closed and opened again without quitting Transmission ...
1350         * change the peerid. It would help sometimes if a stopped event
1351         * was missed to ensure that we didn't think someone was cheating. */
1352        tr_free( tor->peer_id );
1353        tor->peer_id = tr_peerIdNew( );
1354
1355        tor->isRunning = 1;
1356        tr_torrentSetDirty( tor );
1357        tor->preVerifyTotal = tr_cpHaveTotal( &tor->completion );
1358        tr_verifyAdd( tor, checkAndStartCB );
1359    }
1360
1361    tr_globalUnlock( tor->session );
1362}
1363
1364void
1365tr_torrentStart( tr_torrent * tor )
1366{
1367    if( tr_isTorrent( tor ) )
1368        torrentStart( tor );
1369}
1370
1371static void
1372torrentRecheckDoneImpl( void * vtor )
1373{
1374    tr_torrent * tor = vtor;
1375
1376    assert( tr_isTorrent( tor ) );
1377    tr_torrentRecheckCompleteness( tor );
1378
1379    if( tor->preVerifyTotal && !tr_cpHaveTotal( &tor->completion ) )
1380    {
1381        tr_torrentSetLocalError( tor, _( "Can't find local data.  Try \"Set Location\" to find it, or restart the torrent to re-download." ) );
1382        tr_torrentStop( tor );
1383    }
1384    else if( tor->startAfterVerify )
1385    {
1386        tor->startAfterVerify = FALSE;
1387
1388        tr_torrentStart( tor );
1389    }
1390}
1391
1392static void
1393torrentRecheckDoneCB( tr_torrent * tor )
1394{
1395    assert( tr_isTorrent( tor ) );
1396
1397    tr_runInEventThread( tor->session, torrentRecheckDoneImpl, tor );
1398}
1399
1400static void
1401verifyTorrent( void * vtor )
1402{
1403    tr_torrent * tor = vtor;
1404
1405    assert( tr_isTorrent( tor ) );
1406    tr_globalLock( tor->session );
1407
1408    /* if the torrent's already being verified, stop it */
1409    tr_verifyRemove( tor );
1410
1411    /* if the torrent's running, stop it & set the restart-after-verify flag */
1412    if( tor->startAfterVerify || tor->isRunning ) {
1413        tr_torrentStop( tor );
1414        tor->startAfterVerify = TRUE;
1415    }
1416
1417    /* add the torrent to the recheck queue */
1418    tor->preVerifyTotal = tr_cpHaveTotal( &tor->completion );
1419    tr_torrentUncheck( tor );
1420    tr_verifyAdd( tor, torrentRecheckDoneCB );
1421
1422    tr_globalUnlock( tor->session );
1423}
1424
1425void
1426tr_torrentVerify( tr_torrent * tor )
1427{
1428    if( tr_isTorrent( tor ) )
1429        tr_runInEventThread( tor->session, verifyTorrent, tor );
1430}
1431
1432void
1433tr_torrentSave( tr_torrent * tor )
1434{
1435    assert( tr_isTorrent( tor ) );
1436
1437    if( tor->isDirty ) {
1438        tor->isDirty = FALSE;
1439        tr_torrentSaveResume( tor );
1440    }
1441}
1442
1443static void
1444stopTorrent( void * vtor )
1445{
1446    tr_torrent * tor = vtor;
1447
1448    assert( tr_isTorrent( tor ) );
1449
1450    tr_verifyRemove( tor );
1451    tr_peerMgrStopTorrent( tor );
1452    tr_announcerTorrentStopped( tor );
1453
1454    tr_fdTorrentClose( tor->session, tor->uniqueId );
1455
1456    if( !tor->isDeleting )
1457        tr_torrentSave( tor );
1458}
1459
1460void
1461tr_torrentStop( tr_torrent * tor )
1462{
1463    assert( tr_isTorrent( tor ) );
1464
1465    if( tr_isTorrent( tor ) )
1466    {
1467        tr_globalLock( tor->session );
1468
1469        tor->isRunning = 0;
1470        tr_torrentSetDirty( tor );
1471        tr_runInEventThread( tor->session, stopTorrent, tor );
1472
1473        tr_globalUnlock( tor->session );
1474    }
1475}
1476
1477static void
1478closeTorrent( void * vtor )
1479{
1480    tr_benc * d;
1481    tr_torrent * tor = vtor;
1482
1483    assert( tr_isTorrent( tor ) );
1484
1485    d = tr_bencListAddDict( &tor->session->removedTorrents, 2 );
1486    tr_bencDictAddInt( d, "id", tor->uniqueId );
1487    tr_bencDictAddInt( d, "date", time( NULL ) );
1488
1489    stopTorrent( tor );
1490
1491    if( tor->isDeleting )
1492    {
1493        tr_metainfoRemoveSaved( tor->session, &tor->info );
1494        tr_torrentRemoveResume( tor );
1495    }
1496
1497    tor->isRunning = 0;
1498    freeTorrent( tor );
1499}
1500
1501void
1502tr_torrentFree( tr_torrent * tor )
1503{
1504    if( tr_isTorrent( tor ) )
1505    {
1506        tr_session * session = tor->session;
1507        assert( tr_isSession( session ) );
1508        tr_globalLock( session );
1509
1510        tr_torrentClearCompletenessCallback( tor );
1511        tr_runInEventThread( session, closeTorrent, tor );
1512
1513        tr_globalUnlock( session );
1514    }
1515}
1516
1517void
1518tr_torrentRemove( tr_torrent * tor )
1519{
1520    assert( tr_isTorrent( tor ) );
1521
1522    tor->isDeleting = 1;
1523    tr_torrentFree( tor );
1524}
1525
1526/**
1527***  Completeness
1528**/
1529
1530static const char *
1531getCompletionString( int type )
1532{
1533    switch( type )
1534    {
1535        /* Translators: this is a minor point that's safe to skip over, but FYI:
1536           "Complete" and "Done" are specific, different terms in Transmission:
1537           "Complete" means we've downloaded every file in the torrent.
1538           "Done" means we're done downloading the files we wanted, but NOT all
1539           that exist */
1540        case TR_PARTIAL_SEED:
1541            return _( "Done" );
1542
1543        case TR_SEED:
1544            return _( "Complete" );
1545
1546        default:
1547            return _( "Incomplete" );
1548    }
1549}
1550
1551static void
1552fireCompletenessChange( tr_torrent       * tor,
1553                        tr_completeness    status )
1554{
1555    assert( tr_isTorrent( tor ) );
1556    assert( ( status == TR_LEECH )
1557         || ( status == TR_SEED )
1558         || ( status == TR_PARTIAL_SEED ) );
1559
1560    if( tor->completeness_func )
1561        tor->completeness_func( tor, status, tor->completeness_func_user_data );
1562}
1563
1564void
1565tr_torrentSetCompletenessCallback( tr_torrent                    * tor,
1566                                   tr_torrent_completeness_func    func,
1567                                   void                          * user_data )
1568{
1569    assert( tr_isTorrent( tor ) );
1570
1571    tor->completeness_func = func;
1572    tor->completeness_func_user_data = user_data;
1573}
1574
1575void
1576tr_torrentSetRatioLimitHitCallback( tr_torrent                     * tor,
1577                                    tr_torrent_ratio_limit_hit_func  func,
1578                                    void                           * user_data )
1579{
1580    assert( tr_isTorrent( tor ) );
1581
1582    tor->ratio_limit_hit_func = func;
1583    tor->ratio_limit_hit_func_user_data = user_data;
1584}
1585
1586void
1587tr_torrentClearCompletenessCallback( tr_torrent * torrent )
1588{
1589    tr_torrentSetCompletenessCallback( torrent, NULL, NULL );
1590}
1591
1592void
1593tr_torrentClearRatioLimitHitCallback( tr_torrent * torrent )
1594{
1595    tr_torrentSetRatioLimitHitCallback( torrent, NULL, NULL );
1596}
1597
1598void
1599tr_torrentRecheckCompleteness( tr_torrent * tor )
1600{
1601    tr_completeness completeness;
1602
1603    assert( tr_isTorrent( tor ) );
1604
1605    tr_torrentLock( tor );
1606
1607    completeness = tr_cpGetStatus( &tor->completion );
1608
1609    if( completeness != tor->completeness )
1610    {
1611        const int recentChange = tor->downloadedCur != 0;
1612
1613        if( recentChange )
1614        {
1615            tr_torinf( tor, _( "State changed from \"%1$s\" to \"%2$s\"" ),
1616                      getCompletionString( tor->completeness ),
1617                      getCompletionString( completeness ) );
1618        }
1619
1620        tor->completeness = completeness;
1621        tor->needsSeedRatioCheck = TRUE;
1622        tr_fdTorrentClose( tor->session, tor->uniqueId );
1623
1624        /* if the torrent is a seed now,
1625         * and the files used to be in the incompleteDir,
1626         * then move them to the destination directory */
1627        if( tr_torrentIsSeed( tor ) && ( tor->currentDir == tor->incompleteDir ) )
1628            tr_torrentSetLocation( tor, tor->downloadDir, TRUE, NULL, NULL );
1629
1630        fireCompletenessChange( tor, completeness );
1631
1632        if( recentChange && ( completeness == TR_SEED ) )
1633        {
1634            tr_announcerTorrentCompleted( tor );
1635
1636            tor->doneDate = tor->anyDate = time( NULL );
1637        }
1638
1639        tr_torrentSetDirty( tor );
1640    }
1641
1642    tr_torrentUnlock( tor );
1643}
1644
1645/**
1646***  File priorities
1647**/
1648
1649void
1650tr_torrentInitFilePriority( tr_torrent *    tor,
1651                            tr_file_index_t fileIndex,
1652                            tr_priority_t   priority )
1653{
1654    tr_piece_index_t i;
1655    tr_file *        file;
1656
1657    assert( tr_isTorrent( tor ) );
1658    assert( fileIndex < tor->info.fileCount );
1659    assert( tr_isPriority( priority ) );
1660
1661    file = &tor->info.files[fileIndex];
1662    file->priority = priority;
1663    for( i = file->firstPiece; i <= file->lastPiece; ++i )
1664        tor->info.pieces[i].priority = calculatePiecePriority( tor, i, fileIndex );
1665}
1666
1667void
1668tr_torrentSetFilePriorities( tr_torrent *      tor,
1669                             tr_file_index_t * files,
1670                             tr_file_index_t   fileCount,
1671                             tr_priority_t     priority )
1672{
1673    tr_file_index_t i;
1674
1675    assert( tr_isTorrent( tor ) );
1676
1677    tr_torrentLock( tor );
1678
1679    for( i = 0; i < fileCount; ++i )
1680        tr_torrentInitFilePriority( tor, files[i], priority );
1681
1682    tr_torrentSetDirty( tor );
1683    tr_torrentUnlock( tor );
1684}
1685
1686tr_priority_t
1687tr_torrentGetFilePriority( const tr_torrent * tor,
1688                           tr_file_index_t    file )
1689{
1690    tr_priority_t ret;
1691
1692    assert( tr_isTorrent( tor ) );
1693
1694    tr_torrentLock( tor );
1695    assert( tor );
1696    assert( file < tor->info.fileCount );
1697    ret = tor->info.files[file].priority;
1698    tr_torrentUnlock( tor );
1699
1700    return ret;
1701}
1702
1703tr_priority_t*
1704tr_torrentGetFilePriorities( const tr_torrent * tor )
1705{
1706    tr_file_index_t i;
1707    tr_priority_t * p;
1708
1709    assert( tr_isTorrent( tor ) );
1710
1711    tr_torrentLock( tor );
1712    p = tr_new0( tr_priority_t, tor->info.fileCount );
1713    for( i = 0; i < tor->info.fileCount; ++i )
1714        p[i] = tor->info.files[i].priority;
1715    tr_torrentUnlock( tor );
1716
1717    return p;
1718}
1719
1720/**
1721***  File DND
1722**/
1723
1724int
1725tr_torrentGetFileDL( const tr_torrent * tor,
1726                     tr_file_index_t    file )
1727{
1728    int doDownload;
1729
1730    assert( tr_isTorrent( tor ) );
1731
1732    tr_torrentLock( tor );
1733
1734    assert( file < tor->info.fileCount );
1735    doDownload = !tor->info.files[file].dnd;
1736
1737    tr_torrentUnlock( tor );
1738    return doDownload != 0;
1739}
1740
1741static void
1742setFileDND( tr_torrent * tor, tr_file_index_t fileIndex, int doDownload )
1743{
1744    tr_file *        file;
1745    const int        dnd = !doDownload;
1746    tr_piece_index_t firstPiece, firstPieceDND;
1747    tr_piece_index_t lastPiece, lastPieceDND;
1748    tr_file_index_t  i;
1749
1750    assert( tr_isTorrent( tor ) );
1751
1752    file = &tor->info.files[fileIndex];
1753    file->dnd = dnd;
1754    firstPiece = file->firstPiece;
1755    lastPiece = file->lastPiece;
1756
1757    /* can't set the first piece to DND unless
1758       every file using that piece is DND */
1759    firstPieceDND = dnd;
1760    if( fileIndex > 0 )
1761    {
1762        for( i = fileIndex - 1; firstPieceDND; --i )
1763        {
1764            if( tor->info.files[i].lastPiece != firstPiece )
1765                break;
1766            firstPieceDND = tor->info.files[i].dnd;
1767            if( !i )
1768                break;
1769        }
1770    }
1771
1772    /* can't set the last piece to DND unless
1773       every file using that piece is DND */
1774    lastPieceDND = dnd;
1775    for( i = fileIndex + 1; lastPieceDND && i < tor->info.fileCount; ++i )
1776    {
1777        if( tor->info.files[i].firstPiece != lastPiece )
1778            break;
1779        lastPieceDND = tor->info.files[i].dnd;
1780    }
1781
1782    if( firstPiece == lastPiece )
1783    {
1784        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
1785    }
1786    else
1787    {
1788        tr_piece_index_t pp;
1789        tor->info.pieces[firstPiece].dnd = firstPieceDND;
1790        tor->info.pieces[lastPiece].dnd = lastPieceDND;
1791        for( pp = firstPiece + 1; pp < lastPiece; ++pp )
1792            tor->info.pieces[pp].dnd = dnd;
1793    }
1794}
1795
1796void
1797tr_torrentInitFileDLs( tr_torrent      * tor,
1798                       tr_file_index_t * files,
1799                       tr_file_index_t   fileCount,
1800                       tr_bool           doDownload )
1801{
1802    tr_file_index_t i;
1803
1804    assert( tr_isTorrent( tor ) );
1805
1806    tr_torrentLock( tor );
1807
1808    for( i=0; i<fileCount; ++i )
1809        setFileDND( tor, files[i], doDownload );
1810    tr_cpInvalidateDND( &tor->completion );
1811    tor->needsSeedRatioCheck = TRUE;
1812
1813    tr_torrentUnlock( tor );
1814}
1815
1816void
1817tr_torrentSetFileDLs( tr_torrent *      tor,
1818                      tr_file_index_t * files,
1819                      tr_file_index_t   fileCount,
1820                      tr_bool           doDownload )
1821{
1822    assert( tr_isTorrent( tor ) );
1823
1824    tr_torrentLock( tor );
1825    tr_torrentInitFileDLs( tor, files, fileCount, doDownload );
1826    tr_torrentSetDirty( tor );
1827    tr_torrentUnlock( tor );
1828}
1829
1830/***
1831****
1832***/
1833
1834tr_priority_t
1835tr_torrentGetPriority( const tr_torrent * tor )
1836{
1837    assert( tr_isTorrent( tor ) );
1838
1839    return tor->bandwidth->priority;
1840}
1841
1842void
1843tr_torrentSetPriority( tr_torrent * tor, tr_priority_t priority )
1844{
1845    assert( tr_isTorrent( tor ) );
1846    assert( tr_isPriority( priority ) );
1847
1848    if( tor->bandwidth->priority != priority )
1849    {
1850        tor->bandwidth->priority = priority;
1851
1852        tr_torrentSetDirty( tor );
1853    }
1854}
1855
1856/***
1857****
1858***/
1859
1860void
1861tr_torrentSetPeerLimit( tr_torrent * tor,
1862                        uint16_t     maxConnectedPeers )
1863{
1864    assert( tr_isTorrent( tor ) );
1865
1866    if ( tor->maxConnectedPeers != maxConnectedPeers )
1867    {
1868        tor->maxConnectedPeers = maxConnectedPeers;
1869
1870        tr_torrentSetDirty( tor );
1871    }
1872}
1873
1874uint16_t
1875tr_torrentGetPeerLimit( const tr_torrent * tor )
1876{
1877    assert( tr_isTorrent( tor ) );
1878
1879    return tor->maxConnectedPeers;
1880}
1881
1882/***
1883****
1884***/
1885
1886tr_block_index_t
1887_tr_block( const tr_torrent * tor,
1888           tr_piece_index_t   index,
1889           uint32_t           offset )
1890{
1891    tr_block_index_t ret;
1892
1893    assert( tr_isTorrent( tor ) );
1894
1895    ret = index;
1896    ret *= ( tor->info.pieceSize / tor->blockSize );
1897    ret += offset / tor->blockSize;
1898    return ret;
1899}
1900
1901tr_bool
1902tr_torrentReqIsValid( const tr_torrent * tor,
1903                      tr_piece_index_t   index,
1904                      uint32_t           offset,
1905                      uint32_t           length )
1906{
1907    int err = 0;
1908
1909    assert( tr_isTorrent( tor ) );
1910
1911    if( index >= tor->info.pieceCount )
1912        err = 1;
1913    else if( length < 1 )
1914        err = 2;
1915    else if( ( offset + length ) > tr_torPieceCountBytes( tor, index ) )
1916        err = 3;
1917    else if( length > MAX_BLOCK_SIZE )
1918        err = 4;
1919    else if( tr_pieceOffset( tor, index, offset, length ) > tor->info.totalSize )
1920        err = 5;
1921
1922    if( err ) tr_tordbg( tor, "index %lu offset %lu length %lu err %d\n",
1923                              (unsigned long)index,
1924                              (unsigned long)offset,
1925                              (unsigned long)length,
1926                              err );
1927
1928    return !err;
1929}
1930
1931uint64_t
1932tr_pieceOffset( const tr_torrent * tor,
1933                tr_piece_index_t   index,
1934                uint32_t           offset,
1935                uint32_t           length )
1936{
1937    uint64_t ret;
1938
1939    assert( tr_isTorrent( tor ) );
1940
1941    ret = tor->info.pieceSize;
1942    ret *= index;
1943    ret += offset;
1944    ret += length;
1945    return ret;
1946}
1947
1948/***
1949****
1950***/
1951
1952void
1953tr_torrentSetPieceChecked( tr_torrent        * tor,
1954                           tr_piece_index_t    piece,
1955                           tr_bool             isChecked )
1956{
1957    assert( tr_isTorrent( tor ) );
1958
1959    if( isChecked )
1960        tr_bitfieldAdd( &tor->checkedPieces, piece );
1961    else
1962        tr_bitfieldRem( &tor->checkedPieces, piece );
1963}
1964
1965void
1966tr_torrentSetFileChecked( tr_torrent *    tor,
1967                          tr_file_index_t fileIndex,
1968                          tr_bool         isChecked )
1969{
1970    const tr_file *        file = &tor->info.files[fileIndex];
1971    const tr_piece_index_t begin = file->firstPiece;
1972    const tr_piece_index_t end = file->lastPiece + 1;
1973
1974    assert( tr_isTorrent( tor ) );
1975
1976    if( isChecked )
1977        tr_bitfieldAddRange( &tor->checkedPieces, begin, end );
1978    else
1979        tr_bitfieldRemRange( &tor->checkedPieces, begin, end );
1980}
1981
1982tr_bool
1983tr_torrentIsFileChecked( const tr_torrent * tor,
1984                         tr_file_index_t    fileIndex )
1985{
1986    const tr_file *        file = &tor->info.files[fileIndex];
1987    const tr_piece_index_t begin = file->firstPiece;
1988    const tr_piece_index_t end = file->lastPiece + 1;
1989    tr_piece_index_t       i;
1990    tr_bool                isChecked = TRUE;
1991
1992    assert( tr_isTorrent( tor ) );
1993
1994    for( i = begin; isChecked && i < end; ++i )
1995        if( !tr_torrentIsPieceChecked( tor, i ) )
1996            isChecked = FALSE;
1997
1998    return isChecked;
1999}
2000
2001void
2002tr_torrentUncheck( tr_torrent * tor )
2003{
2004    assert( tr_isTorrent( tor ) );
2005
2006    tr_bitfieldRemRange( &tor->checkedPieces, 0, tor->info.pieceCount );
2007}
2008
2009int
2010tr_torrentCountUncheckedPieces( const tr_torrent * tor )
2011{
2012    assert( tr_isTorrent( tor ) );
2013
2014    return tor->info.pieceCount - tr_bitfieldCountTrueBits( &tor->checkedPieces );
2015}
2016
2017time_t*
2018tr_torrentGetMTimes( const tr_torrent * tor, size_t * setme_n )
2019{
2020    size_t       i;
2021    const size_t n = tor->info.fileCount;
2022    time_t *     m = tr_new0( time_t, n );
2023
2024    assert( tr_isTorrent( tor ) );
2025
2026    for( i = 0; i < n; ++i )
2027    {
2028        struct stat sb;
2029        char * path = tr_torrentFindFile( tor, i );
2030        if( ( path != NULL ) && !stat( path, &sb ) && S_ISREG( sb.st_mode ) )
2031        {
2032#ifdef SYS_DARWIN
2033            m[i] = sb.st_mtimespec.tv_sec;
2034#else
2035            m[i] = sb.st_mtime;
2036#endif
2037        }
2038        tr_free( path );
2039    }
2040
2041    *setme_n = n;
2042    return m;
2043}
2044
2045/***
2046****
2047***/
2048
2049tr_announce_list_err
2050tr_torrentSetAnnounceList( tr_torrent *            tor,
2051                           const tr_tracker_info * trackers,
2052                           int                     trackerCount )
2053{
2054    int i, j;
2055    tr_benc metainfo;
2056
2057    assert( tr_isTorrent( tor ) );
2058
2059    /* look for bad URLs */
2060    for( i=0; i<trackerCount; ++i )
2061        if( !tr_httpIsValidURL( trackers[i].announce ) )
2062            return TR_ANNOUNCE_LIST_HAS_BAD;
2063
2064    /* look for duplicates */
2065    for( i=0; i<trackerCount; ++i )
2066        for( j=0; j<trackerCount; ++j )
2067            if( ( i != j ) && ( !strcmp( trackers[i].announce, trackers[j].announce ) ) )
2068                return TR_ANNOUNCE_LIST_HAS_DUPLICATES;
2069
2070    /* save to the .torrent file */
2071    if( !tr_bencLoadFile( &metainfo, TR_FMT_BENC, tor->info.torrent ) )
2072    {
2073        int       i;
2074        int       prevTier = -1;
2075        tr_benc * tier = NULL;
2076        tr_benc * announceList;
2077        tr_info   tmpInfo;
2078
2079        /* remove the old fields */
2080        tr_bencDictRemove( &metainfo, "announce" );
2081        tr_bencDictRemove( &metainfo, "announce-list" );
2082
2083        /* add the new fields */
2084        tr_bencDictAddStr( &metainfo, "announce", trackers[0].announce );
2085        announceList = tr_bencDictAddList( &metainfo, "announce-list", 0 );
2086        for( i = 0; i < trackerCount; ++i ) {
2087            if( prevTier != trackers[i].tier ) {
2088                prevTier = trackers[i].tier;
2089                tier = tr_bencListAddList( announceList, 0 );
2090            }
2091            tr_bencListAddStr( tier, trackers[i].announce );
2092        }
2093
2094        /* try to parse it back again, to make sure it's good */
2095        memset( &tmpInfo, 0, sizeof( tr_info ) );
2096        if( tr_metainfoParse( tor->session, &tmpInfo, &metainfo ) )
2097        {
2098            /* it's good, so keep these new trackers and free the old ones */
2099
2100            tr_info swap;
2101            swap.trackers = tor->info.trackers;
2102            swap.trackerCount = tor->info.trackerCount;
2103            tor->info.trackers = tmpInfo.trackers;
2104            tor->info.trackerCount = tmpInfo.trackerCount;
2105            tmpInfo.trackers = swap.trackers;
2106            tmpInfo.trackerCount = swap.trackerCount;
2107
2108            tr_metainfoFree( &tmpInfo );
2109            tr_bencToFile( &metainfo, TR_FMT_BENC, tor->info.torrent );
2110        }
2111
2112        /* cleanup */
2113        tr_bencFree( &metainfo );
2114
2115        /* tell the announcer to reload this torrent's tracker list */
2116        tr_announcerResetTorrent( tor->session->announcer, tor );
2117    }
2118
2119    return TR_ANNOUNCE_LIST_OK;
2120}
2121
2122/**
2123***
2124**/
2125
2126void
2127tr_torrentSetAddedDate( tr_torrent * tor,
2128                        time_t       t )
2129{
2130    assert( tr_isTorrent( tor ) );
2131
2132    tor->addedDate = t;
2133    tor->anyDate = MAX( tor->anyDate, tor->addedDate );
2134}
2135
2136void
2137tr_torrentSetActivityDate( tr_torrent * tor, time_t t )
2138{
2139    assert( tr_isTorrent( tor ) );
2140
2141    tor->activityDate = t;
2142    tor->anyDate = MAX( tor->anyDate, tor->activityDate );
2143}
2144
2145void
2146tr_torrentSetDoneDate( tr_torrent * tor,
2147                       time_t       t )
2148{
2149    assert( tr_isTorrent( tor ) );
2150
2151    tor->doneDate = t;
2152    tor->anyDate = MAX( tor->anyDate, tor->doneDate );
2153}
2154
2155/**
2156***
2157**/
2158
2159uint64_t
2160tr_torrentGetBytesLeftToAllocate( const tr_torrent * tor )
2161{
2162    tr_file_index_t i;
2163    uint64_t bytesLeft = 0;
2164
2165    assert( tr_isTorrent( tor ) );
2166
2167    for( i=0; i<tor->info.fileCount; ++i )
2168    {
2169        if( !tor->info.files[i].dnd )
2170        {
2171            struct stat sb;
2172            const uint64_t length = tor->info.files[i].length;
2173            char * path = tr_torrentFindFile( tor, i );
2174
2175            bytesLeft += length;
2176
2177            if( ( path != NULL ) && !stat( path, &sb )
2178                                 && S_ISREG( sb.st_mode )
2179                                 && ( (uint64_t)sb.st_size <= length ) )
2180                bytesLeft -= sb.st_size;
2181
2182            tr_free( path );
2183        }
2184    }
2185
2186    return bytesLeft;
2187}
2188
2189/****
2190*****  Removing the torrent's local data
2191****/
2192
2193static int
2194vstrcmp( const void * a, const void * b )
2195{
2196    return strcmp( a, b );
2197}
2198
2199static int
2200compareLongestFirst( const void * a, const void * b )
2201{
2202    const size_t alen = strlen( a );
2203    const size_t blen = strlen( b );
2204
2205    if( alen != blen )
2206        return alen > blen ? -1 : 1;
2207
2208    return vstrcmp( a, b );
2209}
2210
2211static void
2212addDirtyFile( const char  * root,
2213              const char  * filename,
2214              tr_ptrArray * dirtyFolders )
2215{
2216    char * dir = tr_dirname( filename );
2217
2218    /* add the parent folders to dirtyFolders until we reach the root or a known-dirty */
2219    while (     ( dir != NULL )
2220             && ( strlen( root ) <= strlen( dir ) )
2221             && ( tr_ptrArrayFindSorted( dirtyFolders, dir, vstrcmp ) == NULL ) )
2222    {
2223        char * tmp;
2224        tr_ptrArrayInsertSorted( dirtyFolders, tr_strdup( dir ), vstrcmp );
2225
2226        tmp = tr_dirname( dir );
2227        tr_free( dir );
2228        dir = tmp;
2229    }
2230
2231    tr_free( dir );
2232}
2233
2234static void
2235walkLocalData( const tr_torrent * tor,
2236               const char       * root,
2237               const char       * dir,
2238               const char       * base,
2239               tr_ptrArray      * torrentFiles,
2240               tr_ptrArray      * folders,
2241               tr_ptrArray      * dirtyFolders )
2242{
2243    int i;
2244    struct stat sb;
2245    char * buf;
2246
2247    assert( tr_isTorrent( tor ) );
2248
2249    buf = tr_buildPath( dir, base, NULL );
2250    i = stat( buf, &sb );
2251    if( !i )
2252    {
2253        DIR * odir = NULL;
2254
2255        if( S_ISDIR( sb.st_mode ) && ( ( odir = opendir ( buf ) ) ) )
2256        {
2257            struct dirent *d;
2258            tr_ptrArrayInsertSorted( folders, tr_strdup( buf ), vstrcmp );
2259            for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
2260                if( d->d_name && d->d_name[0] != '.' ) /* skip dotfiles */
2261                    walkLocalData( tor, root, buf, d->d_name, torrentFiles, folders, dirtyFolders );
2262            closedir( odir );
2263        }
2264        else if( S_ISREG( sb.st_mode ) && ( sb.st_size > 0 ) )
2265        {
2266            const char * sub = buf + strlen( tor->currentDir ) + strlen( TR_PATH_DELIMITER_STR );
2267            const tr_bool isTorrentFile = tr_ptrArrayFindSorted( torrentFiles, sub, vstrcmp ) != NULL;
2268            if( !isTorrentFile )
2269                addDirtyFile( root, buf, dirtyFolders );
2270        }
2271    }
2272
2273    tr_free( buf );
2274}
2275
2276static void
2277deleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
2278{
2279    int i, n;
2280    char ** s;
2281    tr_file_index_t f;
2282    tr_ptrArray torrentFiles = TR_PTR_ARRAY_INIT;
2283    tr_ptrArray folders      = TR_PTR_ARRAY_INIT;
2284    tr_ptrArray dirtyFolders = TR_PTR_ARRAY_INIT; /* dirty == contains non-torrent files */
2285
2286    const char * firstFile = tor->info.files[0].name;
2287    const char * cpch = strchr( firstFile, TR_PATH_DELIMITER );
2288    char * tmp = cpch ? tr_strndup( firstFile, cpch - firstFile ) : NULL;
2289    char * root = tr_buildPath( tor->currentDir, tmp, NULL );
2290
2291    assert( tr_isTorrent( tor ) );
2292
2293    for( f=0; f<tor->info.fileCount; ++f ) {
2294        tr_ptrArrayInsertSorted( &torrentFiles, tr_strdup( tor->info.files[f].name ), vstrcmp );
2295        tr_ptrArrayInsertSorted( &torrentFiles, tr_torrentBuildPartial( tor, f ), vstrcmp );
2296    }
2297
2298    /* build the set of folders and dirtyFolders */
2299    walkLocalData( tor, root, root, NULL, &torrentFiles, &folders, &dirtyFolders );
2300
2301    /* try to remove entire folders first, so that the recycle bin will be tidy */
2302    s = (char**) tr_ptrArrayPeek( &folders, &n );
2303    for( i=0; i<n; ++i )
2304        if( tr_ptrArrayFindSorted( &dirtyFolders, s[i], vstrcmp ) == NULL )
2305            fileFunc( s[i] );
2306
2307    /* now blow away any remaining torrent files, such as torrent files in dirty folders */
2308    for( f=0; f<tor->info.fileCount; ++f ) {
2309        char * path = tr_buildPath( tor->currentDir, tor->info.files[f].name, NULL );
2310        fileFunc( path );
2311        tr_free( path );
2312    }
2313
2314    /* Now clean out the directories left empty from the previous step.
2315     * Work from deepest to shallowest s.t. lower folders
2316     * won't prevent the upper folders from being deleted */
2317    {
2318        tr_ptrArray cleanFolders = TR_PTR_ARRAY_INIT;
2319        s = (char**) tr_ptrArrayPeek( &folders, &n );
2320        for( i=0; i<n; ++i )
2321            if( tr_ptrArrayFindSorted( &dirtyFolders, s[i], vstrcmp ) == NULL )
2322                tr_ptrArrayInsertSorted( &cleanFolders, s[i], compareLongestFirst );
2323        s = (char**) tr_ptrArrayPeek( &cleanFolders, &n );
2324        for( i=0; i<n; ++i )
2325            fileFunc( s[i] );
2326        tr_ptrArrayDestruct( &cleanFolders, NULL );
2327    }
2328
2329    /* cleanup */
2330    tr_ptrArrayDestruct( &dirtyFolders, tr_free );
2331    tr_ptrArrayDestruct( &folders, tr_free );
2332    tr_ptrArrayDestruct( &torrentFiles, tr_free );
2333    tr_free( root );
2334    tr_free( tmp );
2335}
2336
2337void
2338tr_torrentDeleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
2339{
2340    assert( tr_isTorrent( tor ) );
2341
2342    if( fileFunc == NULL )
2343        fileFunc = remove;
2344
2345    /* close all the files because we're about to delete them */
2346    tr_fdTorrentClose( tor->session, tor->uniqueId );
2347
2348    if( tor->info.fileCount > 1 )
2349        deleteLocalData( tor, fileFunc );
2350    else {
2351        /* torrent only has one file */
2352        char * path = tr_torrentFindFile( tor, 0 );
2353        fileFunc( path );
2354        tr_free( path );
2355    }
2356}
2357
2358/***
2359****
2360***/
2361
2362struct LocationData
2363{
2364    tr_bool move_from_old_location;
2365    int * setme_state;
2366    double * setme_progress;
2367    char * location;
2368    tr_torrent * tor;
2369};
2370
2371static void
2372setLocation( void * vdata )
2373{
2374    tr_bool err = FALSE;
2375    tr_bool verify_needed = FALSE;
2376    struct LocationData * data = vdata;
2377    tr_torrent * tor = data->tor;
2378    const tr_bool do_move = data->move_from_old_location;
2379    const char * location = data->location;
2380    double bytesHandled = 0;
2381
2382    assert( tr_isTorrent( tor ) );
2383
2384    if( strcmp( location, tor->currentDir ) )
2385    {
2386        tr_file_index_t i;
2387
2388        /* bad idea to move files while they're being verified... */
2389        tr_verifyRemove( tor );
2390
2391        /* if the torrent is running, stop it and set a flag to
2392         * restart after we're done */
2393        if( tor->isRunning )
2394        {
2395            tr_torrentStop( tor );
2396            tor->startAfterVerify = TRUE;
2397        }
2398
2399        /* try to move the files.
2400         * FIXME: there are still all kinds of nasty cases, like what
2401         * if the target directory runs out of space halfway through... */
2402        for( i=0; !err && i<tor->info.fileCount; ++i )
2403        {
2404            const tr_file * f = &tor->info.files[i];
2405            const char * oldbase;
2406            char * sub;
2407            if( tr_torrentFindFile2( tor, i, &oldbase, &sub ) )
2408            {
2409                char * oldpath = tr_buildPath( oldbase, sub, NULL );
2410                char * newpath = tr_buildPath( location, sub, NULL );
2411
2412                if( do_move )
2413                {
2414                    errno = 0;
2415                    tr_torinf( tor, "moving \"%s\" to \"%s\"", oldpath, newpath );
2416                    if( rename( oldpath, newpath ) )
2417                    {
2418                        verify_needed = TRUE;
2419                        if( tr_moveFile( oldpath, newpath ) )
2420                        {
2421                            err = TRUE;
2422                            tr_torerr( tor, "error moving \"%s\" to \"%s\": %s",
2423                                            oldpath, newpath, tr_strerror( errno ) );
2424                        }
2425                    }
2426                }
2427
2428                tr_free( newpath );
2429                tr_free( oldpath );
2430                tr_free( sub );
2431            }
2432
2433            if( data->setme_progress )
2434            {
2435                bytesHandled += f->length;
2436                *data->setme_progress = bytesHandled / tor->info.totalSize;
2437            }
2438        }
2439
2440        if( !err )
2441        {
2442            /* blow away the leftover subdirectories in the old location */
2443            if( do_move && verify_needed )
2444                tr_torrentDeleteLocalData( tor, remove );
2445
2446            /* set the new location and reverify */
2447            tr_torrentSetDownloadDir( tor, location );
2448            if( verify_needed )
2449                tr_torrentVerify( tor );
2450        }
2451    }
2452
2453    if( data->setme_state )
2454        *data->setme_state = err ? TR_LOC_ERROR : TR_LOC_DONE;
2455
2456    /* cleanup */
2457    tr_free( data->location );
2458    tr_free( data );
2459}
2460
2461void
2462tr_torrentSetLocation( tr_torrent  * tor,
2463                       const char  * location,
2464                       tr_bool       move_from_old_location,
2465                       double      * setme_progress,
2466                       int         * setme_state )
2467{
2468    struct LocationData * data;
2469
2470    assert( tr_isTorrent( tor ) );
2471
2472    if( setme_state )
2473        *setme_state = TR_LOC_MOVING;
2474    if( setme_progress )
2475        *setme_progress = 0;
2476
2477    /* run this in the libtransmission thread */
2478    data = tr_new( struct LocationData, 1 );
2479    data->tor = tor;
2480    data->location = tr_strdup( location );
2481    data->move_from_old_location = move_from_old_location;
2482    data->setme_state = setme_state;
2483    data->setme_progress = setme_progress;
2484    tr_runInEventThread( tor->session, setLocation, data );
2485}
2486
2487/***
2488****
2489***/
2490
2491void
2492tr_torrentCheckSeedRatio( tr_torrent * tor )
2493{
2494    double seedRatio;
2495
2496    assert( tr_isTorrent( tor ) );
2497
2498    /* if we're seeding and we've reached our seed ratio limit, stop the torrent */
2499    if( tor->isRunning && tr_torrentIsSeed( tor ) && tr_torrentGetSeedRatio( tor, &seedRatio ) )
2500    {
2501        const uint64_t up = tor->uploadedCur + tor->uploadedPrev;
2502        uint64_t down = tor->downloadedCur + tor->downloadedPrev;
2503        double ratio;
2504
2505        /* maybe we're the initial seeder and never downloaded anything... */
2506        if( down == 0 )
2507            down = tr_cpHaveValid( &tor->completion );
2508
2509        ratio = tr_getRatio( up, down );
2510
2511        if( ratio >= seedRatio || ratio == TR_RATIO_INF )
2512        {
2513            tr_torrentStop( tor );
2514
2515            /* set to no ratio limit to allow easy restarting */
2516            tr_torrentSetRatioMode( tor, TR_RATIOLIMIT_UNLIMITED );
2517
2518            /* maybe notify the client */
2519            if( tor->ratio_limit_hit_func != NULL )
2520                tor->ratio_limit_hit_func( tor, tor->ratio_limit_hit_func_user_data );
2521        }
2522    }
2523}
2524
2525/***
2526****
2527***/
2528
2529void
2530tr_torrentFileCompleted( tr_torrent * tor, tr_file_index_t fileNum )
2531{
2532    char * sub;
2533    const char * base;
2534
2535    /* close the file so that we can reopen in read-only mode as needed */
2536    tr_fdFileClose( tor->session, tor, fileNum );
2537
2538    /* if the torrent's filename on disk isn't the same as the one in the metadata,
2539     * then it's been modified to denote that it was a partial file.
2540     * Now that it's complete, use the proper filename. */
2541    if( tr_torrentFindFile2( tor, fileNum, &base, &sub ) )
2542    {
2543        const tr_file * file = &tor->info.files[fileNum];
2544
2545        if( strcmp( sub, file->name ) )
2546        {
2547            char * oldpath = tr_buildPath( base, sub, NULL );
2548            char * newpath = tr_buildPath( base, file->name, NULL );
2549
2550            if( rename( oldpath, newpath ) )
2551                tr_torerr( tor, "Error moving \"%s\" to \"%s\": %s", oldpath, newpath, tr_strerror( errno ) );
2552
2553            tr_free( newpath );
2554            tr_free( oldpath );
2555        }
2556
2557        tr_free( sub );
2558    }
2559}
2560
2561/***
2562****
2563***/
2564
2565static tr_bool
2566fileExists( const char * filename )
2567{
2568    struct stat sb;
2569    const tr_bool ok = !stat( filename, &sb );
2570    return ok;
2571}
2572
2573tr_bool
2574tr_torrentFindFile2( const tr_torrent * tor, tr_file_index_t fileNum,
2575                     const char ** base, char ** subpath )
2576{
2577    char * part;
2578    const tr_file * file;
2579    const char * b = NULL;
2580    const char * s = NULL;
2581
2582    assert( tr_isTorrent( tor ) );
2583    assert( fileNum < tor->info.fileCount );
2584
2585    file = &tor->info.files[fileNum];
2586    part = tr_torrentBuildPartial( tor, fileNum );
2587
2588    if( b == NULL ) {
2589        char * filename = tr_buildPath( tor->downloadDir, file->name, NULL );
2590        if( fileExists( filename ) ) {
2591            b = tor->downloadDir;
2592            s = file->name;
2593        }
2594        tr_free( filename );
2595    }
2596
2597    if( ( b == NULL ) && ( tor->incompleteDir != NULL ) ) {
2598        char * filename = tr_buildPath( tor->incompleteDir, file->name, NULL );
2599        if( fileExists( filename ) ) {
2600            b = tor->incompleteDir;
2601            s = file->name;
2602        }
2603        tr_free( filename );
2604    }
2605
2606    if( ( b == NULL ) && ( tor->incompleteDir != NULL ) ) {
2607        char * filename = tr_buildPath( tor->incompleteDir, part, NULL );
2608        if( fileExists( filename ) ) {
2609            b = tor->incompleteDir;
2610            s = part;
2611        }
2612        tr_free( filename );
2613    }
2614
2615    if( b == NULL) {
2616        char * filename = tr_buildPath( tor->downloadDir, part, NULL );
2617        if( fileExists( filename ) ) {
2618            b = tor->downloadDir;
2619            s = part;
2620        }
2621        tr_free( filename );
2622    }
2623
2624    if( base != NULL )
2625        *base = b;
2626    if( subpath != NULL )
2627        *subpath = tr_strdup( s );
2628
2629    tr_free( part );
2630    return b != NULL;
2631}
2632
2633
2634char*
2635tr_torrentFindFile( const tr_torrent * tor, tr_file_index_t fileNum )
2636{
2637    char * subpath;
2638    char * ret = NULL;
2639    const char * base;
2640
2641    if( tr_torrentFindFile2( tor, fileNum, &base, &subpath ) )
2642    {
2643        ret = tr_buildPath( base, subpath, NULL );
2644        tr_free( subpath );
2645    }
2646
2647    return ret;
2648}
2649
2650/* Decide whether we should be looking for files in downloadDir or incompleteDir. */
2651static void
2652refreshCurrentDir( tr_torrent * tor )
2653{
2654    const char * dir = NULL;
2655    char * sub;
2656
2657    if( tor->incompleteDir == NULL )
2658        dir = tor->downloadDir;
2659    else if( !tr_torrentFindFile2( tor, 0, &dir, &sub ) )
2660        dir = tor->incompleteDir;
2661
2662    assert( dir != NULL );
2663    assert( ( dir == tor->downloadDir ) || ( dir == tor->incompleteDir ) );
2664    tor->currentDir = dir;
2665}
2666
2667char*
2668tr_torrentBuildPartial( const tr_torrent * tor, tr_file_index_t fileNum )
2669{
2670    return tr_strdup_printf( "%s.part", tor->info.files[fileNum].name );
2671}
Note: See TracBrowser for help on using the repository browser.