source: trunk/libtransmission/torrent.c @ 9404

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

on second thought, the other UI's might want this value sooner or later, and it's cheaper to calculate it in the stat function

  • Property svn:keywords set to Date Rev Author Id
File size: 69.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 9404 2009-10-25 18:49:20Z 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    if( !checkSeedRatio || s->ratio >= seedRatio || s->ratio == TR_RATIO_INF ) 
1028        s->percentRatio = 1.0; 
1029    else if( s->ratio == TR_RATIO_NA ) 
1030        s->percentRatio = 0.0; 
1031    else 
1032        s->percentRatio = s->ratio / seedRatio; 
1033
1034    tr_torrentUnlock( tor );
1035
1036    return s;
1037}
1038
1039/***
1040****
1041***/
1042
1043static uint64_t
1044fileBytesCompleted( const tr_torrent * tor, tr_file_index_t index )
1045{
1046    uint64_t total = 0;
1047    const tr_file * f = &tor->info.files[index];
1048
1049    if( f->length )
1050    {
1051        const tr_block_index_t firstBlock = f->offset / tor->blockSize;
1052        const uint64_t lastByte = f->offset + f->length - 1;
1053        const tr_block_index_t lastBlock = lastByte / tor->blockSize;
1054
1055        if( firstBlock == lastBlock )
1056        {
1057            if( tr_cpBlockIsCompleteFast( &tor->completion, firstBlock ) )
1058                total = f->length;
1059        }
1060        else
1061        {
1062            uint32_t i;
1063
1064            /* the first block */
1065            if( tr_cpBlockIsCompleteFast( &tor->completion, firstBlock ) )
1066                total += tor->blockSize - ( f->offset % tor->blockSize );
1067
1068            /* the middle blocks */
1069            if( f->firstPiece == f->lastPiece )
1070            {
1071                for( i=firstBlock+1; i<lastBlock; ++i )
1072                    if( tr_cpBlockIsCompleteFast( &tor->completion, i ) )
1073                        total += tor->blockSize;
1074            }
1075            else
1076            {
1077                int64_t b = 0;
1078                const tr_block_index_t firstBlockOfLastPiece
1079                           = tr_torPieceFirstBlock( tor, f->lastPiece );
1080                const tr_block_index_t lastBlockOfFirstPiece
1081                           = tr_torPieceFirstBlock( tor, f->firstPiece )
1082                             + tr_torPieceCountBlocks( tor, f->firstPiece ) - 1;
1083
1084                /* the rest of the first piece */
1085                for( i=firstBlock+1; i<lastBlock && i<=lastBlockOfFirstPiece; ++i )
1086                    if( tr_cpBlockIsCompleteFast( &tor->completion, i ) )
1087                        ++b;
1088
1089                /* the middle pieces */
1090                if( f->firstPiece + 1 < f->lastPiece )
1091                    for( i=f->firstPiece+1; i<f->lastPiece; ++i )
1092                        b += tor->blockCountInPiece - tr_cpMissingBlocksInPiece( &tor->completion, i );
1093
1094                /* the rest of the last piece */
1095                for( i=firstBlockOfLastPiece; i<lastBlock; ++i )
1096                    if( tr_cpBlockIsCompleteFast( &tor->completion, i ) )
1097                        ++b;
1098
1099                b *= tor->blockSize;
1100                total += b;
1101            }
1102
1103            /* the last block */
1104            if( tr_cpBlockIsCompleteFast( &tor->completion, lastBlock ) )
1105                total += ( f->offset + f->length ) - ( tor->blockSize * lastBlock );
1106        }
1107    }
1108
1109    return total;
1110}
1111
1112tr_file_stat *
1113tr_torrentFiles( const tr_torrent * tor,
1114                 tr_file_index_t *  fileCount )
1115{
1116    tr_file_index_t       i;
1117    const tr_file_index_t n = tor->info.fileCount;
1118    tr_file_stat *        files = tr_new0( tr_file_stat, n );
1119    tr_file_stat *        walk = files;
1120    const tr_bool         isSeed = tor->completeness == TR_SEED;
1121
1122    assert( tr_isTorrent( tor ) );
1123
1124    for( i = 0; i < n; ++i, ++walk )
1125    {
1126        const uint64_t b = isSeed ? tor->info.files[i].length : fileBytesCompleted( tor, i );
1127        walk->bytesCompleted = b;
1128        walk->progress = tr_getRatio( b, tor->info.files[i].length );
1129    }
1130
1131    if( fileCount )
1132        *fileCount = n;
1133
1134    return files;
1135}
1136
1137void
1138tr_torrentFilesFree( tr_file_stat *            files,
1139                     tr_file_index_t fileCount UNUSED )
1140{
1141    tr_free( files );
1142}
1143
1144/***
1145****
1146***/
1147
1148float*
1149tr_torrentWebSpeeds( const tr_torrent * tor )
1150{
1151    return tr_isTorrent( tor )
1152         ? tr_peerMgrWebSpeeds( tor )
1153         : NULL;
1154}
1155
1156tr_peer_stat *
1157tr_torrentPeers( const tr_torrent * tor,
1158                 int *              peerCount )
1159{
1160    tr_peer_stat * ret = NULL;
1161
1162    if( tr_isTorrent( tor ) )
1163        ret = tr_peerMgrPeerStats( tor, peerCount );
1164
1165    return ret;
1166}
1167
1168void
1169tr_torrentPeersFree( tr_peer_stat * peers,
1170                     int peerCount  UNUSED )
1171{
1172    tr_free( peers );
1173}
1174
1175tr_tracker_stat *
1176tr_torrentTrackers( const tr_torrent * torrent,
1177                    int              * setmeTrackerCount )
1178{
1179    assert( tr_isTorrent( torrent ) );
1180
1181    return tr_announcerStats( torrent, setmeTrackerCount );
1182}
1183
1184void
1185tr_torrentTrackersFree( tr_tracker_stat * trackers,
1186                        int trackerCount )
1187{
1188    tr_announcerStatsFree( trackers, trackerCount );
1189}
1190
1191void
1192tr_torrentAvailability( const tr_torrent * tor,
1193                        int8_t *           tab,
1194                        int                size )
1195{
1196    tr_peerMgrTorrentAvailability( tor, tab, size );
1197}
1198
1199void
1200tr_torrentAmountFinished( const tr_torrent * tor,
1201                          float *            tab,
1202                          int                size )
1203{
1204    assert( tr_isTorrent( tor ) );
1205
1206    tr_torrentLock( tor );
1207    tr_cpGetAmountDone( &tor->completion, tab, size );
1208    tr_torrentUnlock( tor );
1209}
1210
1211static void
1212tr_torrentResetTransferStats( tr_torrent * tor )
1213{
1214    assert( tr_isTorrent( tor ) );
1215
1216    tr_torrentLock( tor );
1217
1218    tor->downloadedPrev += tor->downloadedCur;
1219    tor->downloadedCur   = 0;
1220    tor->uploadedPrev   += tor->uploadedCur;
1221    tor->uploadedCur     = 0;
1222    tor->corruptPrev    += tor->corruptCur;
1223    tor->corruptCur      = 0;
1224
1225    tr_torrentSetDirty( tor );
1226
1227    tr_torrentUnlock( tor );
1228}
1229
1230void
1231tr_torrentSetHasPiece( tr_torrent *     tor,
1232                       tr_piece_index_t pieceIndex,
1233                       tr_bool          has )
1234{
1235    assert( tr_isTorrent( tor ) );
1236    assert( pieceIndex < tor->info.pieceCount );
1237
1238    if( has )
1239        tr_cpPieceAdd( &tor->completion, pieceIndex );
1240    else
1241        tr_cpPieceRem( &tor->completion, pieceIndex );
1242}
1243
1244/***
1245****
1246***/
1247
1248static void
1249freeTorrent( tr_torrent * tor )
1250{
1251    tr_torrent * t;
1252    tr_session *  session = tor->session;
1253    tr_info *    inf = &tor->info;
1254
1255    assert( tr_isTorrent( tor ) );
1256    assert( !tor->isRunning );
1257
1258    tr_globalLock( session );
1259
1260    tr_peerMgrRemoveTorrent( tor );
1261
1262    tr_cpDestruct( &tor->completion );
1263
1264    tr_rcDestruct( &tor->swarmSpeed );
1265
1266    tr_announcerUnsubscribe( tor->tiers, tor->tiersSubscription );
1267    tr_announcerRemoveTorrent( session->announcer, tor );
1268
1269    tr_bitfieldDestruct( &tor->checkedPieces );
1270
1271    tr_free( tor->downloadDir );
1272    tr_free( tor->incompleteDir );
1273    tr_free( tor->peer_id );
1274
1275    if( tor == session->torrentList )
1276        session->torrentList = tor->next;
1277    else for( t = session->torrentList; t != NULL; t = t->next ) {
1278        if( t->next == tor ) {
1279            t->next = tor->next;
1280            break;
1281        }
1282    }
1283
1284    assert( session->torrentCount >= 1 );
1285    session->torrentCount--;
1286
1287    tr_bandwidthFree( tor->bandwidth );
1288
1289    tr_metainfoFree( inf );
1290    tr_free( tor );
1291
1292    tr_globalUnlock( session );
1293}
1294
1295/**
1296***  Start/Stop Callback
1297**/
1298
1299static void
1300checkAndStartImpl( void * vtor )
1301{
1302    tr_torrent * tor = vtor;
1303
1304    assert( tr_isTorrent( tor ) );
1305
1306    tr_globalLock( tor->session );
1307
1308    /** If we had local data before, but it's disappeared,
1309        stop the torrent and log an error. */
1310    if( tor->preVerifyTotal && !tr_cpHaveTotal( &tor->completion ) )
1311    {
1312        tr_torrentSetLocalError( tor, _( "Can't find local data.  Try \"Set Location\" to find it, or restart the torrent to re-download." ) );
1313        tr_torrentStop( tor );
1314    }
1315    else
1316    {
1317        const time_t now = time( NULL );
1318        tor->isRunning = TRUE;
1319        tor->needsSeedRatioCheck = TRUE;
1320        tor->error = TR_STAT_OK;
1321        tor->errorString[0] = '\0';
1322        tor->completeness = tr_cpGetStatus( &tor->completion );
1323        tor->startDate = tor->anyDate = now;
1324
1325        tr_torrentResetTransferStats( tor );
1326        tr_announcerTorrentStarted( tor );
1327        tor->dhtAnnounceAt = now + tr_cryptoWeakRandInt( 20 );
1328        tr_peerMgrStartTorrent( tor );
1329    }
1330
1331    tr_globalUnlock( tor->session );
1332}
1333
1334static void
1335checkAndStartCB( tr_torrent * tor )
1336{
1337    assert( tr_isTorrent( tor ) );
1338    assert( tr_isSession( tor->session ) );
1339
1340    tr_runInEventThread( tor->session, checkAndStartImpl, tor );
1341}
1342
1343static void
1344torrentStart( tr_torrent * tor )
1345{
1346    assert( tr_isTorrent( tor ) );
1347
1348    tr_globalLock( tor->session );
1349
1350    if( !tor->isRunning )
1351    {
1352        tr_verifyRemove( tor );
1353
1354        /* corresponds to the peer_id sent as a tracker request parameter.
1355         * one tracker admin says: "When the same torrent is opened and
1356         * closed and opened again without quitting Transmission ...
1357         * change the peerid. It would help sometimes if a stopped event
1358         * was missed to ensure that we didn't think someone was cheating. */
1359        tr_free( tor->peer_id );
1360        tor->peer_id = tr_peerIdNew( );
1361
1362        tor->isRunning = 1;
1363        tr_torrentSetDirty( tor );
1364        tor->preVerifyTotal = tr_cpHaveTotal( &tor->completion );
1365        tr_verifyAdd( tor, checkAndStartCB );
1366    }
1367
1368    tr_globalUnlock( tor->session );
1369}
1370
1371void
1372tr_torrentStart( tr_torrent * tor )
1373{
1374    if( tr_isTorrent( tor ) )
1375        torrentStart( tor );
1376}
1377
1378static void
1379torrentRecheckDoneImpl( void * vtor )
1380{
1381    tr_torrent * tor = vtor;
1382
1383    assert( tr_isTorrent( tor ) );
1384    tr_torrentRecheckCompleteness( tor );
1385
1386    if( tor->preVerifyTotal && !tr_cpHaveTotal( &tor->completion ) )
1387    {
1388        tr_torrentSetLocalError( tor, _( "Can't find local data.  Try \"Set Location\" to find it, or restart the torrent to re-download." ) );
1389        tr_torrentStop( tor );
1390    }
1391    else if( tor->startAfterVerify )
1392    {
1393        tor->startAfterVerify = FALSE;
1394
1395        tr_torrentStart( tor );
1396    }
1397}
1398
1399static void
1400torrentRecheckDoneCB( tr_torrent * tor )
1401{
1402    assert( tr_isTorrent( tor ) );
1403
1404    tr_runInEventThread( tor->session, torrentRecheckDoneImpl, tor );
1405}
1406
1407static void
1408verifyTorrent( void * vtor )
1409{
1410    tr_torrent * tor = vtor;
1411
1412    assert( tr_isTorrent( tor ) );
1413    tr_globalLock( tor->session );
1414
1415    /* if the torrent's already being verified, stop it */
1416    tr_verifyRemove( tor );
1417
1418    /* if the torrent's running, stop it & set the restart-after-verify flag */
1419    if( tor->startAfterVerify || tor->isRunning ) {
1420        tr_torrentStop( tor );
1421        tor->startAfterVerify = TRUE;
1422    }
1423
1424    /* add the torrent to the recheck queue */
1425    tor->preVerifyTotal = tr_cpHaveTotal( &tor->completion );
1426    tr_torrentUncheck( tor );
1427    tr_verifyAdd( tor, torrentRecheckDoneCB );
1428
1429    tr_globalUnlock( tor->session );
1430}
1431
1432void
1433tr_torrentVerify( tr_torrent * tor )
1434{
1435    if( tr_isTorrent( tor ) )
1436        tr_runInEventThread( tor->session, verifyTorrent, tor );
1437}
1438
1439void
1440tr_torrentSave( tr_torrent * tor )
1441{
1442    assert( tr_isTorrent( tor ) );
1443
1444    if( tor->isDirty ) {
1445        tor->isDirty = FALSE;
1446        tr_torrentSaveResume( tor );
1447    }
1448}
1449
1450static void
1451stopTorrent( void * vtor )
1452{
1453    tr_torrent * tor = vtor;
1454
1455    assert( tr_isTorrent( tor ) );
1456
1457    tr_verifyRemove( tor );
1458    tr_peerMgrStopTorrent( tor );
1459    tr_announcerTorrentStopped( tor );
1460
1461    tr_fdTorrentClose( tor->session, tor->uniqueId );
1462
1463    if( !tor->isDeleting )
1464        tr_torrentSave( tor );
1465}
1466
1467void
1468tr_torrentStop( tr_torrent * tor )
1469{
1470    assert( tr_isTorrent( tor ) );
1471
1472    if( tr_isTorrent( tor ) )
1473    {
1474        tr_globalLock( tor->session );
1475
1476        tor->isRunning = 0;
1477        tr_torrentSetDirty( tor );
1478        tr_runInEventThread( tor->session, stopTorrent, tor );
1479
1480        tr_globalUnlock( tor->session );
1481    }
1482}
1483
1484static void
1485closeTorrent( void * vtor )
1486{
1487    tr_benc * d;
1488    tr_torrent * tor = vtor;
1489
1490    assert( tr_isTorrent( tor ) );
1491
1492    d = tr_bencListAddDict( &tor->session->removedTorrents, 2 );
1493    tr_bencDictAddInt( d, "id", tor->uniqueId );
1494    tr_bencDictAddInt( d, "date", time( NULL ) );
1495
1496    stopTorrent( tor );
1497
1498    if( tor->isDeleting )
1499    {
1500        tr_metainfoRemoveSaved( tor->session, &tor->info );
1501        tr_torrentRemoveResume( tor );
1502    }
1503
1504    tor->isRunning = 0;
1505    freeTorrent( tor );
1506}
1507
1508void
1509tr_torrentFree( tr_torrent * tor )
1510{
1511    if( tr_isTorrent( tor ) )
1512    {
1513        tr_session * session = tor->session;
1514        assert( tr_isSession( session ) );
1515        tr_globalLock( session );
1516
1517        tr_torrentClearCompletenessCallback( tor );
1518        tr_runInEventThread( session, closeTorrent, tor );
1519
1520        tr_globalUnlock( session );
1521    }
1522}
1523
1524void
1525tr_torrentRemove( tr_torrent * tor )
1526{
1527    assert( tr_isTorrent( tor ) );
1528
1529    tor->isDeleting = 1;
1530    tr_torrentFree( tor );
1531}
1532
1533/**
1534***  Completeness
1535**/
1536
1537static const char *
1538getCompletionString( int type )
1539{
1540    switch( type )
1541    {
1542        /* Translators: this is a minor point that's safe to skip over, but FYI:
1543           "Complete" and "Done" are specific, different terms in Transmission:
1544           "Complete" means we've downloaded every file in the torrent.
1545           "Done" means we're done downloading the files we wanted, but NOT all
1546           that exist */
1547        case TR_PARTIAL_SEED:
1548            return _( "Done" );
1549
1550        case TR_SEED:
1551            return _( "Complete" );
1552
1553        default:
1554            return _( "Incomplete" );
1555    }
1556}
1557
1558static void
1559fireCompletenessChange( tr_torrent       * tor,
1560                        tr_completeness    status )
1561{
1562    assert( tr_isTorrent( tor ) );
1563    assert( ( status == TR_LEECH )
1564         || ( status == TR_SEED )
1565         || ( status == TR_PARTIAL_SEED ) );
1566
1567    if( tor->completeness_func )
1568        tor->completeness_func( tor, status, tor->completeness_func_user_data );
1569}
1570
1571void
1572tr_torrentSetCompletenessCallback( tr_torrent                    * tor,
1573                                   tr_torrent_completeness_func    func,
1574                                   void                          * user_data )
1575{
1576    assert( tr_isTorrent( tor ) );
1577
1578    tor->completeness_func = func;
1579    tor->completeness_func_user_data = user_data;
1580}
1581
1582void
1583tr_torrentSetRatioLimitHitCallback( tr_torrent                     * tor,
1584                                    tr_torrent_ratio_limit_hit_func  func,
1585                                    void                           * user_data )
1586{
1587    assert( tr_isTorrent( tor ) );
1588
1589    tor->ratio_limit_hit_func = func;
1590    tor->ratio_limit_hit_func_user_data = user_data;
1591}
1592
1593void
1594tr_torrentClearCompletenessCallback( tr_torrent * torrent )
1595{
1596    tr_torrentSetCompletenessCallback( torrent, NULL, NULL );
1597}
1598
1599void
1600tr_torrentClearRatioLimitHitCallback( tr_torrent * torrent )
1601{
1602    tr_torrentSetRatioLimitHitCallback( torrent, NULL, NULL );
1603}
1604
1605void
1606tr_torrentRecheckCompleteness( tr_torrent * tor )
1607{
1608    tr_completeness completeness;
1609
1610    assert( tr_isTorrent( tor ) );
1611
1612    tr_torrentLock( tor );
1613
1614    completeness = tr_cpGetStatus( &tor->completion );
1615
1616    if( completeness != tor->completeness )
1617    {
1618        const int recentChange = tor->downloadedCur != 0;
1619
1620        if( recentChange )
1621        {
1622            tr_torinf( tor, _( "State changed from \"%1$s\" to \"%2$s\"" ),
1623                      getCompletionString( tor->completeness ),
1624                      getCompletionString( completeness ) );
1625        }
1626
1627        tor->completeness = completeness;
1628        tor->needsSeedRatioCheck = TRUE;
1629        tr_fdTorrentClose( tor->session, tor->uniqueId );
1630
1631        /* if the torrent is a seed now,
1632         * and the files used to be in the incompleteDir,
1633         * then move them to the destination directory */
1634        if( tr_torrentIsSeed( tor ) && ( tor->currentDir == tor->incompleteDir ) )
1635            tr_torrentSetLocation( tor, tor->downloadDir, TRUE, NULL, NULL );
1636
1637        fireCompletenessChange( tor, completeness );
1638
1639        if( recentChange && ( completeness == TR_SEED ) )
1640        {
1641            tr_announcerTorrentCompleted( tor );
1642
1643            tor->doneDate = tor->anyDate = time( NULL );
1644        }
1645
1646        tr_torrentSetDirty( tor );
1647    }
1648
1649    tr_torrentUnlock( tor );
1650}
1651
1652/**
1653***  File priorities
1654**/
1655
1656void
1657tr_torrentInitFilePriority( tr_torrent *    tor,
1658                            tr_file_index_t fileIndex,
1659                            tr_priority_t   priority )
1660{
1661    tr_piece_index_t i;
1662    tr_file *        file;
1663
1664    assert( tr_isTorrent( tor ) );
1665    assert( fileIndex < tor->info.fileCount );
1666    assert( tr_isPriority( priority ) );
1667
1668    file = &tor->info.files[fileIndex];
1669    file->priority = priority;
1670    for( i = file->firstPiece; i <= file->lastPiece; ++i )
1671        tor->info.pieces[i].priority = calculatePiecePriority( tor, i, fileIndex );
1672}
1673
1674void
1675tr_torrentSetFilePriorities( tr_torrent *      tor,
1676                             tr_file_index_t * files,
1677                             tr_file_index_t   fileCount,
1678                             tr_priority_t     priority )
1679{
1680    tr_file_index_t i;
1681
1682    assert( tr_isTorrent( tor ) );
1683
1684    tr_torrentLock( tor );
1685
1686    for( i = 0; i < fileCount; ++i )
1687        tr_torrentInitFilePriority( tor, files[i], priority );
1688
1689    tr_torrentSetDirty( tor );
1690    tr_torrentUnlock( tor );
1691}
1692
1693tr_priority_t
1694tr_torrentGetFilePriority( const tr_torrent * tor,
1695                           tr_file_index_t    file )
1696{
1697    tr_priority_t ret;
1698
1699    assert( tr_isTorrent( tor ) );
1700
1701    tr_torrentLock( tor );
1702    assert( tor );
1703    assert( file < tor->info.fileCount );
1704    ret = tor->info.files[file].priority;
1705    tr_torrentUnlock( tor );
1706
1707    return ret;
1708}
1709
1710tr_priority_t*
1711tr_torrentGetFilePriorities( const tr_torrent * tor )
1712{
1713    tr_file_index_t i;
1714    tr_priority_t * p;
1715
1716    assert( tr_isTorrent( tor ) );
1717
1718    tr_torrentLock( tor );
1719    p = tr_new0( tr_priority_t, tor->info.fileCount );
1720    for( i = 0; i < tor->info.fileCount; ++i )
1721        p[i] = tor->info.files[i].priority;
1722    tr_torrentUnlock( tor );
1723
1724    return p;
1725}
1726
1727/**
1728***  File DND
1729**/
1730
1731int
1732tr_torrentGetFileDL( const tr_torrent * tor,
1733                     tr_file_index_t    file )
1734{
1735    int doDownload;
1736
1737    assert( tr_isTorrent( tor ) );
1738
1739    tr_torrentLock( tor );
1740
1741    assert( file < tor->info.fileCount );
1742    doDownload = !tor->info.files[file].dnd;
1743
1744    tr_torrentUnlock( tor );
1745    return doDownload != 0;
1746}
1747
1748static void
1749setFileDND( tr_torrent * tor, tr_file_index_t fileIndex, int doDownload )
1750{
1751    tr_file *        file;
1752    const int        dnd = !doDownload;
1753    tr_piece_index_t firstPiece, firstPieceDND;
1754    tr_piece_index_t lastPiece, lastPieceDND;
1755    tr_file_index_t  i;
1756
1757    assert( tr_isTorrent( tor ) );
1758
1759    file = &tor->info.files[fileIndex];
1760    file->dnd = dnd;
1761    firstPiece = file->firstPiece;
1762    lastPiece = file->lastPiece;
1763
1764    /* can't set the first piece to DND unless
1765       every file using that piece is DND */
1766    firstPieceDND = dnd;
1767    if( fileIndex > 0 )
1768    {
1769        for( i = fileIndex - 1; firstPieceDND; --i )
1770        {
1771            if( tor->info.files[i].lastPiece != firstPiece )
1772                break;
1773            firstPieceDND = tor->info.files[i].dnd;
1774            if( !i )
1775                break;
1776        }
1777    }
1778
1779    /* can't set the last piece to DND unless
1780       every file using that piece is DND */
1781    lastPieceDND = dnd;
1782    for( i = fileIndex + 1; lastPieceDND && i < tor->info.fileCount; ++i )
1783    {
1784        if( tor->info.files[i].firstPiece != lastPiece )
1785            break;
1786        lastPieceDND = tor->info.files[i].dnd;
1787    }
1788
1789    if( firstPiece == lastPiece )
1790    {
1791        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
1792    }
1793    else
1794    {
1795        tr_piece_index_t pp;
1796        tor->info.pieces[firstPiece].dnd = firstPieceDND;
1797        tor->info.pieces[lastPiece].dnd = lastPieceDND;
1798        for( pp = firstPiece + 1; pp < lastPiece; ++pp )
1799            tor->info.pieces[pp].dnd = dnd;
1800    }
1801}
1802
1803void
1804tr_torrentInitFileDLs( tr_torrent      * tor,
1805                       tr_file_index_t * files,
1806                       tr_file_index_t   fileCount,
1807                       tr_bool           doDownload )
1808{
1809    tr_file_index_t i;
1810
1811    assert( tr_isTorrent( tor ) );
1812
1813    tr_torrentLock( tor );
1814
1815    for( i=0; i<fileCount; ++i )
1816        setFileDND( tor, files[i], doDownload );
1817    tr_cpInvalidateDND( &tor->completion );
1818    tor->needsSeedRatioCheck = TRUE;
1819
1820    tr_torrentUnlock( tor );
1821}
1822
1823void
1824tr_torrentSetFileDLs( tr_torrent *      tor,
1825                      tr_file_index_t * files,
1826                      tr_file_index_t   fileCount,
1827                      tr_bool           doDownload )
1828{
1829    assert( tr_isTorrent( tor ) );
1830
1831    tr_torrentLock( tor );
1832    tr_torrentInitFileDLs( tor, files, fileCount, doDownload );
1833    tr_torrentSetDirty( tor );
1834    tr_torrentUnlock( tor );
1835}
1836
1837/***
1838****
1839***/
1840
1841tr_priority_t
1842tr_torrentGetPriority( const tr_torrent * tor )
1843{
1844    assert( tr_isTorrent( tor ) );
1845
1846    return tor->bandwidth->priority;
1847}
1848
1849void
1850tr_torrentSetPriority( tr_torrent * tor, tr_priority_t priority )
1851{
1852    assert( tr_isTorrent( tor ) );
1853    assert( tr_isPriority( priority ) );
1854
1855    if( tor->bandwidth->priority != priority )
1856    {
1857        tor->bandwidth->priority = priority;
1858
1859        tr_torrentSetDirty( tor );
1860    }
1861}
1862
1863/***
1864****
1865***/
1866
1867void
1868tr_torrentSetPeerLimit( tr_torrent * tor,
1869                        uint16_t     maxConnectedPeers )
1870{
1871    assert( tr_isTorrent( tor ) );
1872
1873    if ( tor->maxConnectedPeers != maxConnectedPeers )
1874    {
1875        tor->maxConnectedPeers = maxConnectedPeers;
1876
1877        tr_torrentSetDirty( tor );
1878    }
1879}
1880
1881uint16_t
1882tr_torrentGetPeerLimit( const tr_torrent * tor )
1883{
1884    assert( tr_isTorrent( tor ) );
1885
1886    return tor->maxConnectedPeers;
1887}
1888
1889/***
1890****
1891***/
1892
1893tr_block_index_t
1894_tr_block( const tr_torrent * tor,
1895           tr_piece_index_t   index,
1896           uint32_t           offset )
1897{
1898    tr_block_index_t ret;
1899
1900    assert( tr_isTorrent( tor ) );
1901
1902    ret = index;
1903    ret *= ( tor->info.pieceSize / tor->blockSize );
1904    ret += offset / tor->blockSize;
1905    return ret;
1906}
1907
1908tr_bool
1909tr_torrentReqIsValid( const tr_torrent * tor,
1910                      tr_piece_index_t   index,
1911                      uint32_t           offset,
1912                      uint32_t           length )
1913{
1914    int err = 0;
1915
1916    assert( tr_isTorrent( tor ) );
1917
1918    if( index >= tor->info.pieceCount )
1919        err = 1;
1920    else if( length < 1 )
1921        err = 2;
1922    else if( ( offset + length ) > tr_torPieceCountBytes( tor, index ) )
1923        err = 3;
1924    else if( length > MAX_BLOCK_SIZE )
1925        err = 4;
1926    else if( tr_pieceOffset( tor, index, offset, length ) > tor->info.totalSize )
1927        err = 5;
1928
1929    if( err ) tr_tordbg( tor, "index %lu offset %lu length %lu err %d\n",
1930                              (unsigned long)index,
1931                              (unsigned long)offset,
1932                              (unsigned long)length,
1933                              err );
1934
1935    return !err;
1936}
1937
1938uint64_t
1939tr_pieceOffset( const tr_torrent * tor,
1940                tr_piece_index_t   index,
1941                uint32_t           offset,
1942                uint32_t           length )
1943{
1944    uint64_t ret;
1945
1946    assert( tr_isTorrent( tor ) );
1947
1948    ret = tor->info.pieceSize;
1949    ret *= index;
1950    ret += offset;
1951    ret += length;
1952    return ret;
1953}
1954
1955/***
1956****
1957***/
1958
1959void
1960tr_torrentSetPieceChecked( tr_torrent        * tor,
1961                           tr_piece_index_t    piece,
1962                           tr_bool             isChecked )
1963{
1964    assert( tr_isTorrent( tor ) );
1965
1966    if( isChecked )
1967        tr_bitfieldAdd( &tor->checkedPieces, piece );
1968    else
1969        tr_bitfieldRem( &tor->checkedPieces, piece );
1970}
1971
1972void
1973tr_torrentSetFileChecked( tr_torrent *    tor,
1974                          tr_file_index_t fileIndex,
1975                          tr_bool         isChecked )
1976{
1977    const tr_file *        file = &tor->info.files[fileIndex];
1978    const tr_piece_index_t begin = file->firstPiece;
1979    const tr_piece_index_t end = file->lastPiece + 1;
1980
1981    assert( tr_isTorrent( tor ) );
1982
1983    if( isChecked )
1984        tr_bitfieldAddRange( &tor->checkedPieces, begin, end );
1985    else
1986        tr_bitfieldRemRange( &tor->checkedPieces, begin, end );
1987}
1988
1989tr_bool
1990tr_torrentIsFileChecked( const tr_torrent * tor,
1991                         tr_file_index_t    fileIndex )
1992{
1993    const tr_file *        file = &tor->info.files[fileIndex];
1994    const tr_piece_index_t begin = file->firstPiece;
1995    const tr_piece_index_t end = file->lastPiece + 1;
1996    tr_piece_index_t       i;
1997    tr_bool                isChecked = TRUE;
1998
1999    assert( tr_isTorrent( tor ) );
2000
2001    for( i = begin; isChecked && i < end; ++i )
2002        if( !tr_torrentIsPieceChecked( tor, i ) )
2003            isChecked = FALSE;
2004
2005    return isChecked;
2006}
2007
2008void
2009tr_torrentUncheck( tr_torrent * tor )
2010{
2011    assert( tr_isTorrent( tor ) );
2012
2013    tr_bitfieldRemRange( &tor->checkedPieces, 0, tor->info.pieceCount );
2014}
2015
2016int
2017tr_torrentCountUncheckedPieces( const tr_torrent * tor )
2018{
2019    assert( tr_isTorrent( tor ) );
2020
2021    return tor->info.pieceCount - tr_bitfieldCountTrueBits( &tor->checkedPieces );
2022}
2023
2024time_t*
2025tr_torrentGetMTimes( const tr_torrent * tor, size_t * setme_n )
2026{
2027    size_t       i;
2028    const size_t n = tor->info.fileCount;
2029    time_t *     m = tr_new0( time_t, n );
2030
2031    assert( tr_isTorrent( tor ) );
2032
2033    for( i = 0; i < n; ++i )
2034    {
2035        struct stat sb;
2036        char * path = tr_torrentFindFile( tor, i );
2037        if( ( path != NULL ) && !stat( path, &sb ) && S_ISREG( sb.st_mode ) )
2038        {
2039#ifdef SYS_DARWIN
2040            m[i] = sb.st_mtimespec.tv_sec;
2041#else
2042            m[i] = sb.st_mtime;
2043#endif
2044        }
2045        tr_free( path );
2046    }
2047
2048    *setme_n = n;
2049    return m;
2050}
2051
2052/***
2053****
2054***/
2055
2056tr_announce_list_err
2057tr_torrentSetAnnounceList( tr_torrent *            tor,
2058                           const tr_tracker_info * trackers,
2059                           int                     trackerCount )
2060{
2061    int i, j;
2062    tr_benc metainfo;
2063
2064    assert( tr_isTorrent( tor ) );
2065
2066    /* look for bad URLs */
2067    for( i=0; i<trackerCount; ++i )
2068        if( !tr_httpIsValidURL( trackers[i].announce ) )
2069            return TR_ANNOUNCE_LIST_HAS_BAD;
2070
2071    /* look for duplicates */
2072    for( i=0; i<trackerCount; ++i )
2073        for( j=0; j<trackerCount; ++j )
2074            if( ( i != j ) && ( !strcmp( trackers[i].announce, trackers[j].announce ) ) )
2075                return TR_ANNOUNCE_LIST_HAS_DUPLICATES;
2076
2077    /* save to the .torrent file */
2078    if( !tr_bencLoadFile( &metainfo, TR_FMT_BENC, tor->info.torrent ) )
2079    {
2080        int       i;
2081        int       prevTier = -1;
2082        tr_benc * tier = NULL;
2083        tr_benc * announceList;
2084        tr_info   tmpInfo;
2085
2086        /* remove the old fields */
2087        tr_bencDictRemove( &metainfo, "announce" );
2088        tr_bencDictRemove( &metainfo, "announce-list" );
2089
2090        /* add the new fields */
2091        tr_bencDictAddStr( &metainfo, "announce", trackers[0].announce );
2092        announceList = tr_bencDictAddList( &metainfo, "announce-list", 0 );
2093        for( i = 0; i < trackerCount; ++i ) {
2094            if( prevTier != trackers[i].tier ) {
2095                prevTier = trackers[i].tier;
2096                tier = tr_bencListAddList( announceList, 0 );
2097            }
2098            tr_bencListAddStr( tier, trackers[i].announce );
2099        }
2100
2101        /* try to parse it back again, to make sure it's good */
2102        memset( &tmpInfo, 0, sizeof( tr_info ) );
2103        if( tr_metainfoParse( tor->session, &tmpInfo, &metainfo ) )
2104        {
2105            /* it's good, so keep these new trackers and free the old ones */
2106
2107            tr_info swap;
2108            swap.trackers = tor->info.trackers;
2109            swap.trackerCount = tor->info.trackerCount;
2110            tor->info.trackers = tmpInfo.trackers;
2111            tor->info.trackerCount = tmpInfo.trackerCount;
2112            tmpInfo.trackers = swap.trackers;
2113            tmpInfo.trackerCount = swap.trackerCount;
2114
2115            tr_metainfoFree( &tmpInfo );
2116            tr_bencToFile( &metainfo, TR_FMT_BENC, tor->info.torrent );
2117        }
2118
2119        /* cleanup */
2120        tr_bencFree( &metainfo );
2121
2122        /* tell the announcer to reload this torrent's tracker list */
2123        tr_announcerResetTorrent( tor->session->announcer, tor );
2124    }
2125
2126    return TR_ANNOUNCE_LIST_OK;
2127}
2128
2129/**
2130***
2131**/
2132
2133void
2134tr_torrentSetAddedDate( tr_torrent * tor,
2135                        time_t       t )
2136{
2137    assert( tr_isTorrent( tor ) );
2138
2139    tor->addedDate = t;
2140    tor->anyDate = MAX( tor->anyDate, tor->addedDate );
2141}
2142
2143void
2144tr_torrentSetActivityDate( tr_torrent * tor, time_t t )
2145{
2146    assert( tr_isTorrent( tor ) );
2147
2148    tor->activityDate = t;
2149    tor->anyDate = MAX( tor->anyDate, tor->activityDate );
2150}
2151
2152void
2153tr_torrentSetDoneDate( tr_torrent * tor,
2154                       time_t       t )
2155{
2156    assert( tr_isTorrent( tor ) );
2157
2158    tor->doneDate = t;
2159    tor->anyDate = MAX( tor->anyDate, tor->doneDate );
2160}
2161
2162/**
2163***
2164**/
2165
2166uint64_t
2167tr_torrentGetBytesLeftToAllocate( const tr_torrent * tor )
2168{
2169    tr_file_index_t i;
2170    uint64_t bytesLeft = 0;
2171
2172    assert( tr_isTorrent( tor ) );
2173
2174    for( i=0; i<tor->info.fileCount; ++i )
2175    {
2176        if( !tor->info.files[i].dnd )
2177        {
2178            struct stat sb;
2179            const uint64_t length = tor->info.files[i].length;
2180            char * path = tr_torrentFindFile( tor, i );
2181
2182            bytesLeft += length;
2183
2184            if( ( path != NULL ) && !stat( path, &sb )
2185                                 && S_ISREG( sb.st_mode )
2186                                 && ( (uint64_t)sb.st_size <= length ) )
2187                bytesLeft -= sb.st_size;
2188
2189            tr_free( path );
2190        }
2191    }
2192
2193    return bytesLeft;
2194}
2195
2196/****
2197*****  Removing the torrent's local data
2198****/
2199
2200static int
2201vstrcmp( const void * a, const void * b )
2202{
2203    return strcmp( a, b );
2204}
2205
2206static int
2207compareLongestFirst( const void * a, const void * b )
2208{
2209    const size_t alen = strlen( a );
2210    const size_t blen = strlen( b );
2211
2212    if( alen != blen )
2213        return alen > blen ? -1 : 1;
2214
2215    return vstrcmp( a, b );
2216}
2217
2218static void
2219addDirtyFile( const char  * root,
2220              const char  * filename,
2221              tr_ptrArray * dirtyFolders )
2222{
2223    char * dir = tr_dirname( filename );
2224
2225    /* add the parent folders to dirtyFolders until we reach the root or a known-dirty */
2226    while (     ( dir != NULL )
2227             && ( strlen( root ) <= strlen( dir ) )
2228             && ( tr_ptrArrayFindSorted( dirtyFolders, dir, vstrcmp ) == NULL ) )
2229    {
2230        char * tmp;
2231        tr_ptrArrayInsertSorted( dirtyFolders, tr_strdup( dir ), vstrcmp );
2232
2233        tmp = tr_dirname( dir );
2234        tr_free( dir );
2235        dir = tmp;
2236    }
2237
2238    tr_free( dir );
2239}
2240
2241static void
2242walkLocalData( const tr_torrent * tor,
2243               const char       * root,
2244               const char       * dir,
2245               const char       * base,
2246               tr_ptrArray      * torrentFiles,
2247               tr_ptrArray      * folders,
2248               tr_ptrArray      * dirtyFolders )
2249{
2250    int i;
2251    struct stat sb;
2252    char * buf;
2253
2254    assert( tr_isTorrent( tor ) );
2255
2256    buf = tr_buildPath( dir, base, NULL );
2257    i = stat( buf, &sb );
2258    if( !i )
2259    {
2260        DIR * odir = NULL;
2261
2262        if( S_ISDIR( sb.st_mode ) && ( ( odir = opendir ( buf ) ) ) )
2263        {
2264            struct dirent *d;
2265            tr_ptrArrayInsertSorted( folders, tr_strdup( buf ), vstrcmp );
2266            for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
2267                if( d->d_name && d->d_name[0] != '.' ) /* skip dotfiles */
2268                    walkLocalData( tor, root, buf, d->d_name, torrentFiles, folders, dirtyFolders );
2269            closedir( odir );
2270        }
2271        else if( S_ISREG( sb.st_mode ) && ( sb.st_size > 0 ) )
2272        {
2273            const char * sub = buf + strlen( tor->currentDir ) + strlen( TR_PATH_DELIMITER_STR );
2274            const tr_bool isTorrentFile = tr_ptrArrayFindSorted( torrentFiles, sub, vstrcmp ) != NULL;
2275            if( !isTorrentFile )
2276                addDirtyFile( root, buf, dirtyFolders );
2277        }
2278    }
2279
2280    tr_free( buf );
2281}
2282
2283static void
2284deleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
2285{
2286    int i, n;
2287    char ** s;
2288    tr_file_index_t f;
2289    tr_ptrArray torrentFiles = TR_PTR_ARRAY_INIT;
2290    tr_ptrArray folders      = TR_PTR_ARRAY_INIT;
2291    tr_ptrArray dirtyFolders = TR_PTR_ARRAY_INIT; /* dirty == contains non-torrent files */
2292
2293    const char * firstFile = tor->info.files[0].name;
2294    const char * cpch = strchr( firstFile, TR_PATH_DELIMITER );
2295    char * tmp = cpch ? tr_strndup( firstFile, cpch - firstFile ) : NULL;
2296    char * root = tr_buildPath( tor->currentDir, tmp, NULL );
2297
2298    assert( tr_isTorrent( tor ) );
2299
2300    for( f=0; f<tor->info.fileCount; ++f ) {
2301        tr_ptrArrayInsertSorted( &torrentFiles, tr_strdup( tor->info.files[f].name ), vstrcmp );
2302        tr_ptrArrayInsertSorted( &torrentFiles, tr_torrentBuildPartial( tor, f ), vstrcmp );
2303    }
2304
2305    /* build the set of folders and dirtyFolders */
2306    walkLocalData( tor, root, root, NULL, &torrentFiles, &folders, &dirtyFolders );
2307
2308    /* try to remove entire folders first, so that the recycle bin will be tidy */
2309    s = (char**) tr_ptrArrayPeek( &folders, &n );
2310    for( i=0; i<n; ++i )
2311        if( tr_ptrArrayFindSorted( &dirtyFolders, s[i], vstrcmp ) == NULL )
2312            fileFunc( s[i] );
2313
2314    /* now blow away any remaining torrent files, such as torrent files in dirty folders */
2315    for( f=0; f<tor->info.fileCount; ++f ) {
2316        char * path = tr_buildPath( tor->currentDir, tor->info.files[f].name, NULL );
2317        fileFunc( path );
2318        tr_free( path );
2319    }
2320
2321    /* Now clean out the directories left empty from the previous step.
2322     * Work from deepest to shallowest s.t. lower folders
2323     * won't prevent the upper folders from being deleted */
2324    {
2325        tr_ptrArray cleanFolders = TR_PTR_ARRAY_INIT;
2326        s = (char**) tr_ptrArrayPeek( &folders, &n );
2327        for( i=0; i<n; ++i )
2328            if( tr_ptrArrayFindSorted( &dirtyFolders, s[i], vstrcmp ) == NULL )
2329                tr_ptrArrayInsertSorted( &cleanFolders, s[i], compareLongestFirst );
2330        s = (char**) tr_ptrArrayPeek( &cleanFolders, &n );
2331        for( i=0; i<n; ++i )
2332            fileFunc( s[i] );
2333        tr_ptrArrayDestruct( &cleanFolders, NULL );
2334    }
2335
2336    /* cleanup */
2337    tr_ptrArrayDestruct( &dirtyFolders, tr_free );
2338    tr_ptrArrayDestruct( &folders, tr_free );
2339    tr_ptrArrayDestruct( &torrentFiles, tr_free );
2340    tr_free( root );
2341    tr_free( tmp );
2342}
2343
2344void
2345tr_torrentDeleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
2346{
2347    assert( tr_isTorrent( tor ) );
2348
2349    if( fileFunc == NULL )
2350        fileFunc = remove;
2351
2352    /* close all the files because we're about to delete them */
2353    tr_fdTorrentClose( tor->session, tor->uniqueId );
2354
2355    if( tor->info.fileCount > 1 )
2356        deleteLocalData( tor, fileFunc );
2357    else {
2358        /* torrent only has one file */
2359        char * path = tr_torrentFindFile( tor, 0 );
2360        fileFunc( path );
2361        tr_free( path );
2362    }
2363}
2364
2365/***
2366****
2367***/
2368
2369struct LocationData
2370{
2371    tr_bool move_from_old_location;
2372    int * setme_state;
2373    double * setme_progress;
2374    char * location;
2375    tr_torrent * tor;
2376};
2377
2378static void
2379setLocation( void * vdata )
2380{
2381    tr_bool err = FALSE;
2382    tr_bool verify_needed = FALSE;
2383    struct LocationData * data = vdata;
2384    tr_torrent * tor = data->tor;
2385    const tr_bool do_move = data->move_from_old_location;
2386    const char * location = data->location;
2387    double bytesHandled = 0;
2388
2389    assert( tr_isTorrent( tor ) );
2390
2391    if( strcmp( location, tor->currentDir ) )
2392    {
2393        tr_file_index_t i;
2394
2395        /* bad idea to move files while they're being verified... */
2396        tr_verifyRemove( tor );
2397
2398        /* if the torrent is running, stop it and set a flag to
2399         * restart after we're done */
2400        if( tor->isRunning )
2401        {
2402            tr_torrentStop( tor );
2403            tor->startAfterVerify = TRUE;
2404        }
2405
2406        /* try to move the files.
2407         * FIXME: there are still all kinds of nasty cases, like what
2408         * if the target directory runs out of space halfway through... */
2409        for( i=0; !err && i<tor->info.fileCount; ++i )
2410        {
2411            const tr_file * f = &tor->info.files[i];
2412            const char * oldbase;
2413            char * sub;
2414            if( tr_torrentFindFile2( tor, i, &oldbase, &sub ) )
2415            {
2416                char * oldpath = tr_buildPath( oldbase, sub, NULL );
2417                char * newpath = tr_buildPath( location, sub, NULL );
2418
2419                if( do_move )
2420                {
2421                    errno = 0;
2422                    tr_torinf( tor, "moving \"%s\" to \"%s\"", oldpath, newpath );
2423                    if( rename( oldpath, newpath ) )
2424                    {
2425                        verify_needed = TRUE;
2426                        if( tr_moveFile( oldpath, newpath ) )
2427                        {
2428                            err = TRUE;
2429                            tr_torerr( tor, "error moving \"%s\" to \"%s\": %s",
2430                                            oldpath, newpath, tr_strerror( errno ) );
2431                        }
2432                    }
2433                }
2434
2435                tr_free( newpath );
2436                tr_free( oldpath );
2437                tr_free( sub );
2438            }
2439
2440            if( data->setme_progress )
2441            {
2442                bytesHandled += f->length;
2443                *data->setme_progress = bytesHandled / tor->info.totalSize;
2444            }
2445        }
2446
2447        if( !err )
2448        {
2449            /* blow away the leftover subdirectories in the old location */
2450            if( do_move && verify_needed )
2451                tr_torrentDeleteLocalData( tor, remove );
2452
2453            /* set the new location and reverify */
2454            tr_torrentSetDownloadDir( tor, location );
2455            if( verify_needed )
2456                tr_torrentVerify( tor );
2457        }
2458    }
2459
2460    if( data->setme_state )
2461        *data->setme_state = err ? TR_LOC_ERROR : TR_LOC_DONE;
2462
2463    /* cleanup */
2464    tr_free( data->location );
2465    tr_free( data );
2466}
2467
2468void
2469tr_torrentSetLocation( tr_torrent  * tor,
2470                       const char  * location,
2471                       tr_bool       move_from_old_location,
2472                       double      * setme_progress,
2473                       int         * setme_state )
2474{
2475    struct LocationData * data;
2476
2477    assert( tr_isTorrent( tor ) );
2478
2479    if( setme_state )
2480        *setme_state = TR_LOC_MOVING;
2481    if( setme_progress )
2482        *setme_progress = 0;
2483
2484    /* run this in the libtransmission thread */
2485    data = tr_new( struct LocationData, 1 );
2486    data->tor = tor;
2487    data->location = tr_strdup( location );
2488    data->move_from_old_location = move_from_old_location;
2489    data->setme_state = setme_state;
2490    data->setme_progress = setme_progress;
2491    tr_runInEventThread( tor->session, setLocation, data );
2492}
2493
2494/***
2495****
2496***/
2497
2498void
2499tr_torrentCheckSeedRatio( tr_torrent * tor )
2500{
2501    double seedRatio;
2502
2503    assert( tr_isTorrent( tor ) );
2504
2505    /* if we're seeding and we've reached our seed ratio limit, stop the torrent */
2506    if( tor->isRunning && tr_torrentIsSeed( tor ) && tr_torrentGetSeedRatio( tor, &seedRatio ) )
2507    {
2508        const uint64_t up = tor->uploadedCur + tor->uploadedPrev;
2509        uint64_t down = tor->downloadedCur + tor->downloadedPrev;
2510        double ratio;
2511
2512        /* maybe we're the initial seeder and never downloaded anything... */
2513        if( down == 0 )
2514            down = tr_cpHaveValid( &tor->completion );
2515
2516        ratio = tr_getRatio( up, down );
2517
2518        if( ratio >= seedRatio || ratio == TR_RATIO_INF )
2519        {
2520            tr_torrentStop( tor );
2521
2522            /* set to no ratio limit to allow easy restarting */
2523            tr_torrentSetRatioMode( tor, TR_RATIOLIMIT_UNLIMITED );
2524
2525            /* maybe notify the client */
2526            if( tor->ratio_limit_hit_func != NULL )
2527                tor->ratio_limit_hit_func( tor, tor->ratio_limit_hit_func_user_data );
2528        }
2529    }
2530}
2531
2532/***
2533****
2534***/
2535
2536void
2537tr_torrentFileCompleted( tr_torrent * tor, tr_file_index_t fileNum )
2538{
2539    char * sub;
2540    const char * base;
2541
2542    /* close the file so that we can reopen in read-only mode as needed */
2543    tr_fdFileClose( tor->session, tor, fileNum );
2544
2545    /* if the torrent's filename on disk isn't the same as the one in the metadata,
2546     * then it's been modified to denote that it was a partial file.
2547     * Now that it's complete, use the proper filename. */
2548    if( tr_torrentFindFile2( tor, fileNum, &base, &sub ) )
2549    {
2550        const tr_file * file = &tor->info.files[fileNum];
2551
2552        if( strcmp( sub, file->name ) )
2553        {
2554            char * oldpath = tr_buildPath( base, sub, NULL );
2555            char * newpath = tr_buildPath( base, file->name, NULL );
2556
2557            if( rename( oldpath, newpath ) )
2558                tr_torerr( tor, "Error moving \"%s\" to \"%s\": %s", oldpath, newpath, tr_strerror( errno ) );
2559
2560            tr_free( newpath );
2561            tr_free( oldpath );
2562        }
2563
2564        tr_free( sub );
2565    }
2566}
2567
2568/***
2569****
2570***/
2571
2572static tr_bool
2573fileExists( const char * filename )
2574{
2575    struct stat sb;
2576    const tr_bool ok = !stat( filename, &sb );
2577    return ok;
2578}
2579
2580tr_bool
2581tr_torrentFindFile2( const tr_torrent * tor, tr_file_index_t fileNum,
2582                     const char ** base, char ** subpath )
2583{
2584    char * part;
2585    const tr_file * file;
2586    const char * b = NULL;
2587    const char * s = NULL;
2588
2589    assert( tr_isTorrent( tor ) );
2590    assert( fileNum < tor->info.fileCount );
2591
2592    file = &tor->info.files[fileNum];
2593    part = tr_torrentBuildPartial( tor, fileNum );
2594
2595    if( b == NULL ) {
2596        char * filename = tr_buildPath( tor->downloadDir, file->name, NULL );
2597        if( fileExists( filename ) ) {
2598            b = tor->downloadDir;
2599            s = file->name;
2600        }
2601        tr_free( filename );
2602    }
2603
2604    if( ( b == NULL ) && ( tor->incompleteDir != NULL ) ) {
2605        char * filename = tr_buildPath( tor->incompleteDir, file->name, NULL );
2606        if( fileExists( filename ) ) {
2607            b = tor->incompleteDir;
2608            s = file->name;
2609        }
2610        tr_free( filename );
2611    }
2612
2613    if( ( b == NULL ) && ( tor->incompleteDir != NULL ) ) {
2614        char * filename = tr_buildPath( tor->incompleteDir, part, NULL );
2615        if( fileExists( filename ) ) {
2616            b = tor->incompleteDir;
2617            s = part;
2618        }
2619        tr_free( filename );
2620    }
2621
2622    if( b == NULL) {
2623        char * filename = tr_buildPath( tor->downloadDir, part, NULL );
2624        if( fileExists( filename ) ) {
2625            b = tor->downloadDir;
2626            s = part;
2627        }
2628        tr_free( filename );
2629    }
2630
2631    if( base != NULL )
2632        *base = b;
2633    if( subpath != NULL )
2634        *subpath = tr_strdup( s );
2635
2636    tr_free( part );
2637    return b != NULL;
2638}
2639
2640
2641char*
2642tr_torrentFindFile( const tr_torrent * tor, tr_file_index_t fileNum )
2643{
2644    char * subpath;
2645    char * ret = NULL;
2646    const char * base;
2647
2648    if( tr_torrentFindFile2( tor, fileNum, &base, &subpath ) )
2649    {
2650        ret = tr_buildPath( base, subpath, NULL );
2651        tr_free( subpath );
2652    }
2653
2654    return ret;
2655}
2656
2657/* Decide whether we should be looking for files in downloadDir or incompleteDir. */
2658static void
2659refreshCurrentDir( tr_torrent * tor )
2660{
2661    const char * dir = NULL;
2662    char * sub;
2663
2664    if( tor->incompleteDir == NULL )
2665        dir = tor->downloadDir;
2666    else if( !tr_torrentFindFile2( tor, 0, &dir, &sub ) )
2667        dir = tor->incompleteDir;
2668
2669    assert( dir != NULL );
2670    assert( ( dir == tor->downloadDir ) || ( dir == tor->incompleteDir ) );
2671    tor->currentDir = dir;
2672}
2673
2674char*
2675tr_torrentBuildPartial( const tr_torrent * tor, tr_file_index_t fileNum )
2676{
2677    return tr_strdup_printf( "%s.part", tor->info.files[fileNum].name );
2678}
Note: See TracBrowser for help on using the repository browser.