source: trunk/libtransmission/torrent.c @ 9335

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

(trunk libT) make the ".part" suffix an optional feature as per BMW's request. Clean up the code a little.

  • Property svn:keywords set to Date Rev Author Id
File size: 68.5 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 9335 2009-10-20 03:14:44Z charles $
11 */
12
13#include <sys/types.h> /* stat */
14#include <sys/stat.h> /* stat */
15#include <unistd.h> /* stat */
16#include <dirent.h>
17
18#include <assert.h>
19#include <limits.h> /* INT_MAX */
20#include <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_sessionIsIncompleteDirEnabled( session ) )
585        tor->incompleteDir = tr_strdup( tr_sessionGetIncompleteDir( session ) );
586
587    tor->lastPieceSize = info->totalSize % info->pieceSize;
588
589    if( !tor->lastPieceSize )
590        tor->lastPieceSize = info->pieceSize;
591
592    tor->lastBlockSize = info->totalSize % tor->blockSize;
593
594    if( !tor->lastBlockSize )
595        tor->lastBlockSize = tor->blockSize;
596
597    tor->blockCount =
598        ( info->totalSize + tor->blockSize - 1 ) / tor->blockSize;
599
600    tor->blockCountInPiece =
601        info->pieceSize / tor->blockSize;
602
603    tor->blockCountInLastPiece =
604        ( tor->lastPieceSize + tor->blockSize - 1 ) / tor->blockSize;
605
606    /* check our work */
607    assert( ( info->pieceSize % tor->blockSize ) == 0 );
608    t = info->pieceCount - 1;
609    t *= info->pieceSize;
610    t += tor->lastPieceSize;
611    assert( t == info->totalSize );
612    t = tor->blockCount - 1;
613    t *= tor->blockSize;
614    t += tor->lastBlockSize;
615    assert( t == info->totalSize );
616    t = info->pieceCount - 1;
617    t *= tor->blockCountInPiece;
618    t += tor->blockCountInLastPiece;
619    assert( t == (uint64_t)tor->blockCount );
620
621    tr_cpConstruct( &tor->completion, tor );
622
623    tr_torrentInitFilePieces( tor );
624
625    tr_rcConstruct( &tor->swarmSpeed );
626
627    tr_sha1( tor->obfuscatedHash, "req2", 4,
628             info->hash, SHA_DIGEST_LENGTH,
629             NULL );
630
631    tr_peerMgrAddTorrent( session->peerMgr, tor );
632
633    assert( !tor->downloadedCur );
634    assert( !tor->uploadedCur );
635
636    tr_ctorInitTorrentPriorities( ctor, tor );
637
638    tr_ctorInitTorrentWanted( ctor, tor );
639
640    tor->error = TR_STAT_OK;
641
642    tr_bitfieldConstruct( &tor->checkedPieces, tor->info.pieceCount );
643    tr_torrentUncheck( tor );
644
645    tr_torrentSetAddedDate( tor, time( NULL ) ); /* this is a default value to be
646                                                    overwritten by the resume file */
647
648    loaded = tr_torrentLoadResume( tor, ~0, ctor );
649
650    refreshCurrentDir( tor );
651
652    doStart = tor->isRunning;
653    tor->isRunning = 0;
654
655    if( !( loaded & TR_FR_SPEEDLIMIT ) )
656    {
657        tr_torrentUseSpeedLimit( tor, TR_UP, FALSE );
658        tr_torrentSetSpeedLimit( tor, TR_UP, tr_sessionGetSpeedLimit( tor->session, TR_UP ) );
659        tr_torrentUseSpeedLimit( tor, TR_DOWN, FALSE );
660        tr_torrentSetSpeedLimit( tor, TR_DOWN, tr_sessionGetSpeedLimit( tor->session, TR_DOWN ) );
661        tr_torrentUseSessionLimits( tor, TRUE );
662    }
663
664    if( !( loaded & TR_FR_RATIOLIMIT ) )
665    {
666        tr_torrentSetRatioMode( tor, TR_RATIOLIMIT_GLOBAL );
667        tr_torrentSetRatioLimit( tor, tr_sessionGetRatioLimit( tor->session ) );
668    }
669
670    tor->completeness = tr_cpGetStatus( &tor->completion );
671
672    {
673        tr_torrent * it = NULL;
674        tr_torrent * last = NULL;
675        while( ( it = tr_torrentNext( session, it ) ) )
676            last = it;
677
678        if( !last )
679            session->torrentList = tor;
680        else
681            last->next = tor;
682        ++session->torrentCount;
683    }
684
685    tr_globalUnlock( session );
686
687    /* maybe save our own copy of the metainfo */
688    if( tr_ctorGetSave( ctor ) )
689    {
690        const tr_benc * val;
691        if( !tr_ctorGetMetainfo( ctor, &val ) )
692        {
693            const char * filename = tor->info.torrent;
694            tr_bencToFile( val, TR_FMT_BENC, filename );
695            tr_sessionSetTorrentFile( tor->session, tor->info.hashString, filename );
696        }
697    }
698
699    tor->tiers = tr_announcerAddTorrent( session->announcer, tor );
700    tor->tiersSubscription = tr_announcerSubscribe( tor->tiers, onTrackerResponse, tor );
701
702    tr_metainfoMigrate( session, &tor->info );
703
704    if( doStart )
705        torrentStart( tor );
706}
707
708tr_parse_result
709tr_torrentParse( const tr_ctor * ctor, tr_info * setmeInfo )
710{
711    int             doFree;
712    tr_bool         didParse;
713    tr_info         tmp;
714    const tr_benc * metainfo;
715    tr_session    * session = tr_ctorGetSession( ctor );
716    tr_parse_result result = TR_PARSE_OK;
717
718    if( setmeInfo == NULL )
719        setmeInfo = &tmp;
720    memset( setmeInfo, 0, sizeof( tr_info ) );
721
722    if( tr_ctorGetMetainfo( ctor, &metainfo ) )
723        return TR_PARSE_ERR;
724
725    didParse = tr_metainfoParse( session, setmeInfo, metainfo );
726    doFree = didParse && ( setmeInfo == &tmp );
727
728    if( !didParse )
729        result = TR_PARSE_ERR;
730
731    if( didParse && !getBlockSize( setmeInfo->pieceSize ) )
732        result = TR_PARSE_ERR;
733
734    if( didParse && session && tr_torrentExists( session, setmeInfo->hash ) )
735        result = TR_PARSE_DUPLICATE;
736
737    if( doFree )
738        tr_metainfoFree( setmeInfo );
739
740    return result;
741}
742
743tr_torrent *
744tr_torrentNew( const tr_ctor  * ctor,
745               int            * setmeError )
746{
747    int          err;
748    tr_info      tmpInfo;
749    tr_torrent * tor = NULL;
750
751    assert( ctor != NULL );
752    assert( tr_isSession( tr_ctorGetSession( ctor ) ) );
753
754    err = tr_torrentParse( ctor, &tmpInfo );
755    if( !err )
756    {
757        tor = tr_new0( tr_torrent, 1 );
758        tor->info = tmpInfo;
759        torrentRealInit( tor, ctor );
760    }
761    else if( setmeError )
762    {
763        *setmeError = err;
764    }
765
766    return tor;
767}
768
769/**
770***
771**/
772
773void
774tr_torrentSetDownloadDir( tr_torrent * tor, const char * path )
775{
776    assert( tr_isTorrent( tor  ) );
777
778    if( !path || !tor->downloadDir || strcmp( path, tor->downloadDir ) )
779    {
780        tr_free( tor->downloadDir );
781        tor->downloadDir = tr_strdup( path );
782        tr_torrentSetDirty( tor );
783    }
784
785    refreshCurrentDir( tor );
786}
787
788const char*
789tr_torrentGetDownloadDir( const tr_torrent * tor )
790{
791    assert( tr_isTorrent( tor  ) );
792
793    return tor->downloadDir;
794}
795
796void
797tr_torrentChangeMyPort( tr_torrent * tor )
798{
799    assert( tr_isTorrent( tor  ) );
800
801    tr_announcerChangeMyPort( tor );
802}
803
804static TR_INLINE void
805tr_torrentManualUpdateImpl( void * vtor )
806{
807    tr_torrent * tor = vtor;
808
809    assert( tr_isTorrent( tor  ) );
810
811    if( tor->isRunning )
812        tr_announcerManualAnnounce( tor );
813}
814
815void
816tr_torrentManualUpdate( tr_torrent * tor )
817{
818    assert( tr_isTorrent( tor  ) );
819
820    tr_runInEventThread( tor->session, tr_torrentManualUpdateImpl, tor );
821}
822
823tr_bool
824tr_torrentCanManualUpdate( const tr_torrent * tor )
825{
826    return ( tr_isTorrent( tor  ) )
827        && ( tor->isRunning )
828        && ( tr_announcerCanManualAnnounce( tor ) );
829}
830
831const tr_info *
832tr_torrentInfo( const tr_torrent * tor )
833{
834    return tr_isTorrent( tor ) ? &tor->info : NULL;
835}
836
837const tr_stat *
838tr_torrentStatCached( tr_torrent * tor )
839{
840    const time_t now = time( NULL );
841
842    return tr_isTorrent( tor ) && ( now == tor->lastStatTime )
843         ? &tor->stats
844         : tr_torrentStat( tor );
845}
846
847void
848tr_torrentSetVerifyState( tr_torrent * tor, tr_verify_state state )
849{
850    assert( tr_isTorrent( tor ) );
851    assert( state==TR_VERIFY_NONE || state==TR_VERIFY_WAIT || state==TR_VERIFY_NOW );
852
853    tor->verifyState = state;
854    tor->anyDate = time( NULL );
855}
856
857tr_torrent_activity
858tr_torrentGetActivity( tr_torrent * tor )
859{
860    assert( tr_isTorrent( tor ) );
861
862    tr_torrentRecheckCompleteness( tor );
863
864    if( tor->verifyState == TR_VERIFY_NOW )
865        return TR_STATUS_CHECK;
866    if( tor->verifyState == TR_VERIFY_WAIT )
867        return TR_STATUS_CHECK_WAIT;
868    if( !tor->isRunning )
869        return TR_STATUS_STOPPED;
870    if( tor->completeness == TR_LEECH )
871        return TR_STATUS_DOWNLOAD;
872
873    return TR_STATUS_SEED;
874}
875
876const tr_stat *
877tr_torrentStat( tr_torrent * tor )
878{
879    tr_stat *               s;
880    int                     usableSeeds = 0;
881    uint64_t                now;
882    double                  downloadedForRatio, seedRatio=0;
883    double                  d;
884    tr_bool                 checkSeedRatio;
885
886    if( !tor )
887        return NULL;
888
889    assert( tr_isTorrent( tor ) );
890    tr_torrentLock( tor );
891
892    tor->lastStatTime = time( NULL );
893
894    s = &tor->stats;
895    s->id = tor->uniqueId;
896    s->activity = tr_torrentGetActivity( tor );
897    s->error = tor->error;
898    memcpy( s->errorString, tor->errorString, sizeof( s->errorString ) );
899
900    s->manualAnnounceTime = tr_announcerNextManualAnnounce( tor );
901
902    tr_peerMgrTorrentStats( tor,
903                            &s->peersKnown,
904                            &s->peersConnected,
905                            &usableSeeds,
906                            &s->webseedsSendingToUs,
907                            &s->peersSendingToUs,
908                            &s->peersGettingFromUs,
909                            s->peersFrom );
910
911    now = tr_date( );
912    d = tr_peerMgrGetWebseedSpeed( tor, now );
913    s->swarmSpeed         = tr_rcRate( &tor->swarmSpeed, now );
914    s->rawUploadSpeed     = tr_bandwidthGetRawSpeed  ( tor->bandwidth, now, TR_UP );
915    s->pieceUploadSpeed   = tr_bandwidthGetPieceSpeed( tor->bandwidth, now, TR_UP );
916    s->rawDownloadSpeed   = d + tr_bandwidthGetRawSpeed  ( tor->bandwidth, now, TR_DOWN );
917    s->pieceDownloadSpeed = d + tr_bandwidthGetPieceSpeed( tor->bandwidth, now, TR_DOWN );
918
919    usableSeeds += tor->info.webseedCount;
920
921    s->percentComplete = tr_cpPercentComplete ( &tor->completion );
922
923    s->percentDone   = tr_cpPercentDone  ( &tor->completion );
924    s->leftUntilDone = tr_cpLeftUntilDone( &tor->completion );
925    s->sizeWhenDone  = tr_cpSizeWhenDone ( &tor->completion );
926
927    s->recheckProgress = s->activity == TR_STATUS_CHECK
928                       ? 1.0 -
929                         ( tr_torrentCountUncheckedPieces( tor ) /
930                           (double) tor->info.pieceCount )
931                       : 0.0;
932
933    s->activityDate = tor->activityDate;
934    s->addedDate    = tor->addedDate;
935    s->doneDate     = tor->doneDate;
936    s->startDate    = tor->startDate;
937
938    s->corruptEver     = tor->corruptCur    + tor->corruptPrev;
939    s->downloadedEver  = tor->downloadedCur + tor->downloadedPrev;
940    s->uploadedEver    = tor->uploadedCur   + tor->uploadedPrev;
941    s->haveValid       = tr_cpHaveValid( &tor->completion );
942    s->haveUnchecked   = tr_cpHaveTotal( &tor->completion ) - s->haveValid;
943
944    if( usableSeeds > 0 )
945    {
946        s->desiredAvailable = s->leftUntilDone;
947    }
948    else if( !s->leftUntilDone || !s->peersConnected )
949    {
950        s->desiredAvailable = 0;
951    }
952    else
953    {
954        tr_piece_index_t i;
955        tr_bitfield *    peerPieces = tr_peerMgrGetAvailable( tor );
956        s->desiredAvailable = 0;
957        for( i = 0; i < tor->info.pieceCount; ++i )
958            if( !tor->info.pieces[i].dnd && tr_bitfieldHasFast( peerPieces, i ) )
959                s->desiredAvailable += tr_cpMissingBlocksInPiece( &tor->completion, i );
960        s->desiredAvailable *= tor->blockSize;
961        tr_bitfieldFree( peerPieces );
962    }
963
964    downloadedForRatio = s->downloadedEver ? s->downloadedEver : s->haveValid;
965    s->ratio = tr_getRatio( s->uploadedEver, downloadedForRatio );
966
967    checkSeedRatio = tr_torrentGetSeedRatio( tor, &seedRatio );
968
969    switch( s->activity )
970    {
971        case TR_STATUS_DOWNLOAD:
972            if( s->leftUntilDone > s->desiredAvailable )
973                s->eta = TR_ETA_NOT_AVAIL;
974            else if( s->pieceDownloadSpeed < 0.1 )
975                s->eta = TR_ETA_UNKNOWN;
976            else {
977                /* etaSpeed exists because if we pieceDownloadSpeed directly,
978                 * brief fluctuations cause the ETA to jump all over the place.
979                 * so, etaSpeed is a smoothed-out version of pieceDownloadSpeed
980                 * to dampen the effect of fluctuations */
981                if( ( tor->etaSpeedCalculatedAt + 800 ) < now ) {
982                    tor->etaSpeedCalculatedAt = now;
983                    tor->etaSpeed = (fabs(tor->etaSpeed)<0.0001)
984                        ? s->pieceDownloadSpeed /* if no previous speed, no need to smooth */
985                        : 0.8*tor->etaSpeed + 0.2*s->pieceDownloadSpeed; /* smooth across 5 readings */
986                }
987                s->eta = s->leftUntilDone / tor->etaSpeed / 1024.0;
988            }
989            break;
990
991        case TR_STATUS_SEED:
992            if( checkSeedRatio )
993            {
994                if( s->pieceUploadSpeed < 0.1 )
995                    s->eta = TR_ETA_UNKNOWN;
996                else
997                    s->eta = (downloadedForRatio * (seedRatio - s->ratio)) / s->pieceUploadSpeed / 1024.0;
998            }
999            else
1000                s->eta = TR_ETA_NOT_AVAIL;
1001            break;
1002
1003        default:
1004            s->eta = TR_ETA_NOT_AVAIL;
1005            break;
1006    }
1007
1008    if( !checkSeedRatio || s->ratio >= seedRatio || s->ratio == TR_RATIO_INF )
1009        s->percentRatio = 1.0;
1010    else if( s->ratio == TR_RATIO_NA )
1011        s->percentRatio = 0.0;
1012    else
1013        s->percentRatio = s->ratio / seedRatio;
1014
1015    tr_torrentUnlock( tor );
1016
1017    return s;
1018}
1019
1020/***
1021****
1022***/
1023
1024static uint64_t
1025fileBytesCompleted( const tr_torrent * tor, tr_file_index_t index )
1026{
1027    uint64_t total = 0;
1028    const tr_file * f = &tor->info.files[index];
1029
1030    if( f->length )
1031    {
1032        const tr_block_index_t firstBlock = f->offset / tor->blockSize;
1033        const uint64_t lastByte = f->offset + f->length - 1;
1034        const tr_block_index_t lastBlock = lastByte / tor->blockSize;
1035
1036        if( firstBlock == lastBlock )
1037        {
1038            if( tr_cpBlockIsCompleteFast( &tor->completion, firstBlock ) )
1039                total = f->length;
1040        }
1041        else
1042        {
1043            uint32_t i;
1044
1045            /* the first block */
1046            if( tr_cpBlockIsCompleteFast( &tor->completion, firstBlock ) )
1047                total += tor->blockSize - ( f->offset % tor->blockSize );
1048
1049            /* the middle blocks */
1050            if( f->firstPiece == f->lastPiece )
1051            {
1052                for( i=firstBlock+1; i<lastBlock; ++i )
1053                    if( tr_cpBlockIsCompleteFast( &tor->completion, i ) )
1054                        total += tor->blockSize;
1055            }
1056            else
1057            {
1058                int64_t b = 0;
1059                const tr_block_index_t firstBlockOfLastPiece
1060                           = tr_torPieceFirstBlock( tor, f->lastPiece );
1061                const tr_block_index_t lastBlockOfFirstPiece
1062                           = tr_torPieceFirstBlock( tor, f->firstPiece )
1063                             + tr_torPieceCountBlocks( tor, f->firstPiece ) - 1;
1064
1065                /* the rest of the first piece */
1066                for( i=firstBlock+1; i<lastBlock && i<=lastBlockOfFirstPiece; ++i )
1067                    if( tr_cpBlockIsCompleteFast( &tor->completion, i ) )
1068                        ++b;
1069
1070                /* the middle pieces */
1071                if( f->firstPiece + 1 < f->lastPiece )
1072                    for( i=f->firstPiece+1; i<f->lastPiece; ++i )
1073                        b += tor->blockCountInPiece - tr_cpMissingBlocksInPiece( &tor->completion, i );
1074
1075                /* the rest of the last piece */
1076                for( i=firstBlockOfLastPiece; i<lastBlock; ++i )
1077                    if( tr_cpBlockIsCompleteFast( &tor->completion, i ) )
1078                        ++b;
1079
1080                b *= tor->blockSize;
1081                total += b;
1082            }
1083
1084            /* the last block */
1085            if( tr_cpBlockIsCompleteFast( &tor->completion, lastBlock ) )
1086                total += ( f->offset + f->length ) - ( tor->blockSize * lastBlock );
1087        }
1088    }
1089
1090    return total;
1091}
1092
1093tr_file_stat *
1094tr_torrentFiles( const tr_torrent * tor,
1095                 tr_file_index_t *  fileCount )
1096{
1097    tr_file_index_t       i;
1098    const tr_file_index_t n = tor->info.fileCount;
1099    tr_file_stat *        files = tr_new0( tr_file_stat, n );
1100    tr_file_stat *        walk = files;
1101    const tr_bool         isSeed = tor->completeness == TR_SEED;
1102
1103    assert( tr_isTorrent( tor ) );
1104
1105    for( i = 0; i < n; ++i, ++walk )
1106    {
1107        const uint64_t b = isSeed ? tor->info.files[i].length : fileBytesCompleted( tor, i );
1108        walk->bytesCompleted = b;
1109        walk->progress = tr_getRatio( b, tor->info.files[i].length );
1110    }
1111
1112    if( fileCount )
1113        *fileCount = n;
1114
1115    return files;
1116}
1117
1118void
1119tr_torrentFilesFree( tr_file_stat *            files,
1120                     tr_file_index_t fileCount UNUSED )
1121{
1122    tr_free( files );
1123}
1124
1125/***
1126****
1127***/
1128
1129float*
1130tr_torrentWebSpeeds( const tr_torrent * tor )
1131{
1132    return tr_isTorrent( tor )
1133         ? tr_peerMgrWebSpeeds( tor )
1134         : NULL;
1135}
1136
1137tr_peer_stat *
1138tr_torrentPeers( const tr_torrent * tor,
1139                 int *              peerCount )
1140{
1141    tr_peer_stat * ret = NULL;
1142
1143    if( tr_isTorrent( tor ) )
1144        ret = tr_peerMgrPeerStats( tor, peerCount );
1145
1146    return ret;
1147}
1148
1149void
1150tr_torrentPeersFree( tr_peer_stat * peers,
1151                     int peerCount  UNUSED )
1152{
1153    tr_free( peers );
1154}
1155
1156tr_tracker_stat *
1157tr_torrentTrackers( const tr_torrent * torrent,
1158                    int              * setmeTrackerCount )
1159{
1160    assert( tr_isTorrent( torrent ) );
1161
1162    return tr_announcerStats( torrent, setmeTrackerCount );
1163}
1164
1165void
1166tr_torrentTrackersFree( tr_tracker_stat * trackers,
1167                        int trackerCount )
1168{
1169    tr_announcerStatsFree( trackers, trackerCount );
1170}
1171
1172void
1173tr_torrentAvailability( const tr_torrent * tor,
1174                        int8_t *           tab,
1175                        int                size )
1176{
1177    tr_peerMgrTorrentAvailability( tor, tab, size );
1178}
1179
1180void
1181tr_torrentAmountFinished( const tr_torrent * tor,
1182                          float *            tab,
1183                          int                size )
1184{
1185    assert( tr_isTorrent( tor ) );
1186
1187    tr_torrentLock( tor );
1188    tr_cpGetAmountDone( &tor->completion, tab, size );
1189    tr_torrentUnlock( tor );
1190}
1191
1192static void
1193tr_torrentResetTransferStats( tr_torrent * tor )
1194{
1195    assert( tr_isTorrent( tor ) );
1196
1197    tr_torrentLock( tor );
1198
1199    tor->downloadedPrev += tor->downloadedCur;
1200    tor->downloadedCur   = 0;
1201    tor->uploadedPrev   += tor->uploadedCur;
1202    tor->uploadedCur     = 0;
1203    tor->corruptPrev    += tor->corruptCur;
1204    tor->corruptCur      = 0;
1205
1206    tr_torrentSetDirty( tor );
1207
1208    tr_torrentUnlock( tor );
1209}
1210
1211void
1212tr_torrentSetHasPiece( tr_torrent *     tor,
1213                       tr_piece_index_t pieceIndex,
1214                       tr_bool          has )
1215{
1216    assert( tr_isTorrent( tor ) );
1217    assert( pieceIndex < tor->info.pieceCount );
1218
1219    if( has )
1220        tr_cpPieceAdd( &tor->completion, pieceIndex );
1221    else
1222        tr_cpPieceRem( &tor->completion, pieceIndex );
1223}
1224
1225/***
1226****
1227***/
1228
1229static void
1230freeTorrent( tr_torrent * tor )
1231{
1232    tr_torrent * t;
1233    tr_session *  session = tor->session;
1234    tr_info *    inf = &tor->info;
1235
1236    assert( tr_isTorrent( tor ) );
1237    assert( !tor->isRunning );
1238
1239    tr_globalLock( session );
1240
1241    tr_peerMgrRemoveTorrent( tor );
1242
1243    tr_cpDestruct( &tor->completion );
1244
1245    tr_rcDestruct( &tor->swarmSpeed );
1246
1247    tr_announcerUnsubscribe( tor->tiers, tor->tiersSubscription );
1248    tr_announcerRemoveTorrent( session->announcer, tor );
1249
1250    tr_bitfieldDestruct( &tor->checkedPieces );
1251
1252    tr_free( tor->downloadDir );
1253    tr_free( tor->incompleteDir );
1254    tr_free( tor->peer_id );
1255
1256    if( tor == session->torrentList )
1257        session->torrentList = tor->next;
1258    else for( t = session->torrentList; t != NULL; t = t->next ) {
1259        if( t->next == tor ) {
1260            t->next = tor->next;
1261            break;
1262        }
1263    }
1264
1265    assert( session->torrentCount >= 1 );
1266    session->torrentCount--;
1267
1268    tr_bandwidthFree( tor->bandwidth );
1269
1270    tr_metainfoFree( inf );
1271    tr_free( tor );
1272
1273    tr_globalUnlock( session );
1274}
1275
1276/**
1277***  Start/Stop Callback
1278**/
1279
1280static void
1281checkAndStartImpl( void * vtor )
1282{
1283    tr_torrent * tor = vtor;
1284
1285    assert( tr_isTorrent( tor ) );
1286
1287    tr_globalLock( tor->session );
1288
1289    /** If we had local data before, but it's disappeared,
1290        stop the torrent and log an error. */
1291    if( tor->preVerifyTotal && !tr_cpHaveTotal( &tor->completion ) )
1292    {
1293        tr_torrentSetLocalError( tor, _( "Can't find local data.  Try \"Set Location\" to find it, or restart the torrent to re-download." ) );
1294        tr_torrentStop( tor );
1295    }
1296    else
1297    {
1298        const time_t now = time( NULL );
1299        tor->isRunning = TRUE;
1300        tor->needsSeedRatioCheck = TRUE;
1301        tor->error = TR_STAT_OK;
1302        tor->errorString[0] = '\0';
1303        tor->completeness = tr_cpGetStatus( &tor->completion );
1304        tor->startDate = tor->anyDate = now;
1305
1306        tr_torrentResetTransferStats( tor );
1307        tr_announcerTorrentStarted( tor );
1308        tor->dhtAnnounceAt = now + tr_cryptoWeakRandInt( 20 );
1309        tr_peerMgrStartTorrent( tor );
1310    }
1311
1312    tr_globalUnlock( tor->session );
1313}
1314
1315static void
1316checkAndStartCB( tr_torrent * tor )
1317{
1318    assert( tr_isTorrent( tor ) );
1319    assert( tr_isSession( tor->session ) );
1320
1321    tr_runInEventThread( tor->session, checkAndStartImpl, tor );
1322}
1323
1324static void
1325torrentStart( tr_torrent * tor )
1326{
1327    assert( tr_isTorrent( tor ) );
1328
1329    tr_globalLock( tor->session );
1330
1331    if( !tor->isRunning )
1332    {
1333        tr_verifyRemove( tor );
1334
1335        /* corresponds to the peer_id sent as a tracker request parameter.
1336         * one tracker admin says: "When the same torrent is opened and
1337         * closed and opened again without quitting Transmission ...
1338         * change the peerid. It would help sometimes if a stopped event
1339         * was missed to ensure that we didn't think someone was cheating. */
1340        tr_free( tor->peer_id );
1341        tor->peer_id = tr_peerIdNew( );
1342
1343        tor->isRunning = 1;
1344        tor->preVerifyTotal = tr_cpHaveTotal( &tor->completion );
1345        tr_verifyAdd( tor, checkAndStartCB );
1346    }
1347
1348    tr_globalUnlock( tor->session );
1349}
1350
1351void
1352tr_torrentStart( tr_torrent * tor )
1353{
1354    if( tr_isTorrent( tor ) )
1355        torrentStart( tor );
1356}
1357
1358static void
1359torrentRecheckDoneImpl( void * vtor )
1360{
1361    tr_torrent * tor = vtor;
1362
1363    assert( tr_isTorrent( tor ) );
1364    tr_torrentRecheckCompleteness( tor );
1365
1366    if( tor->preVerifyTotal && !tr_cpHaveTotal( &tor->completion ) )
1367    {
1368        tr_torrentSetLocalError( tor, _( "Can't find local data.  Try \"Set Location\" to find it, or restart the torrent to re-download." ) );
1369        tr_torrentStop( tor );
1370    }
1371    else if( tor->startAfterVerify )
1372    {
1373        tor->startAfterVerify = FALSE;
1374
1375        tr_torrentStart( tor );
1376    }
1377}
1378
1379static void
1380torrentRecheckDoneCB( tr_torrent * tor )
1381{
1382    assert( tr_isTorrent( tor ) );
1383
1384    tr_runInEventThread( tor->session, torrentRecheckDoneImpl, tor );
1385}
1386
1387static void
1388verifyTorrent( void * vtor )
1389{
1390    tr_torrent * tor = vtor;
1391
1392    assert( tr_isTorrent( tor ) );
1393    tr_globalLock( tor->session );
1394
1395    /* if the torrent's already being verified, stop it */
1396    tr_verifyRemove( tor );
1397
1398    /* if the torrent's running, stop it & set the restart-after-verify flag */
1399    if( tor->startAfterVerify || tor->isRunning ) {
1400        tr_torrentStop( tor );
1401        tor->startAfterVerify = TRUE;
1402    }
1403
1404    /* add the torrent to the recheck queue */
1405    tor->preVerifyTotal = tr_cpHaveTotal( &tor->completion );
1406    tr_torrentUncheck( tor );
1407    tr_verifyAdd( tor, torrentRecheckDoneCB );
1408
1409    tr_globalUnlock( tor->session );
1410}
1411
1412void
1413tr_torrentVerify( tr_torrent * tor )
1414{
1415    if( tr_isTorrent( tor ) )
1416        tr_runInEventThread( tor->session, verifyTorrent, tor );
1417}
1418
1419void
1420tr_torrentSave( tr_torrent * tor )
1421{
1422    assert( tr_isTorrent( tor ) );
1423
1424    if( tor->isDirty ) {
1425        tor->isDirty = FALSE;
1426        tr_torrentSaveResume( tor );
1427    }
1428}
1429
1430static void
1431stopTorrent( void * vtor )
1432{
1433    tr_torrent * tor = vtor;
1434
1435    assert( tr_isTorrent( tor ) );
1436
1437    tr_verifyRemove( tor );
1438    tr_peerMgrStopTorrent( tor );
1439    tr_announcerTorrentStopped( tor );
1440
1441    tr_fdTorrentClose( tor->uniqueId );
1442
1443    if( !tor->isDeleting )
1444        tr_torrentSave( tor );
1445}
1446
1447void
1448tr_torrentStop( tr_torrent * tor )
1449{
1450    assert( tr_isTorrent( tor ) );
1451
1452    if( tr_isTorrent( tor ) )
1453    {
1454        tr_globalLock( tor->session );
1455
1456        tor->isRunning = 0;
1457        tr_runInEventThread( tor->session, stopTorrent, tor );
1458
1459        tr_globalUnlock( tor->session );
1460    }
1461}
1462
1463static void
1464closeTorrent( void * vtor )
1465{
1466    tr_benc * d;
1467    tr_torrent * tor = vtor;
1468
1469    assert( tr_isTorrent( tor ) );
1470
1471    d = tr_bencListAddDict( &tor->session->removedTorrents, 2 );
1472    tr_bencDictAddInt( d, "id", tor->uniqueId );
1473    tr_bencDictAddInt( d, "date", time( NULL ) );
1474
1475    stopTorrent( tor );
1476
1477    if( tor->isDeleting )
1478    {
1479        tr_metainfoRemoveSaved( tor->session, &tor->info );
1480        tr_torrentRemoveResume( tor );
1481    }
1482
1483    tor->isRunning = 0;
1484    freeTorrent( tor );
1485}
1486
1487void
1488tr_torrentFree( tr_torrent * tor )
1489{
1490    if( tr_isTorrent( tor ) )
1491    {
1492        tr_session * session = tor->session;
1493        assert( tr_isSession( session ) );
1494        tr_globalLock( session );
1495
1496        tr_torrentClearCompletenessCallback( tor );
1497        tr_runInEventThread( session, closeTorrent, tor );
1498
1499        tr_globalUnlock( session );
1500    }
1501}
1502
1503void
1504tr_torrentRemove( tr_torrent * tor )
1505{
1506    assert( tr_isTorrent( tor ) );
1507
1508    tor->isDeleting = 1;
1509    tr_torrentFree( tor );
1510}
1511
1512/**
1513***  Completeness
1514**/
1515
1516static const char *
1517getCompletionString( int type )
1518{
1519    switch( type )
1520    {
1521        /* Translators: this is a minor point that's safe to skip over, but FYI:
1522           "Complete" and "Done" are specific, different terms in Transmission:
1523           "Complete" means we've downloaded every file in the torrent.
1524           "Done" means we're done downloading the files we wanted, but NOT all
1525           that exist */
1526        case TR_PARTIAL_SEED:
1527            return _( "Done" );
1528
1529        case TR_SEED:
1530            return _( "Complete" );
1531
1532        default:
1533            return _( "Incomplete" );
1534    }
1535}
1536
1537static void
1538fireCompletenessChange( tr_torrent       * tor,
1539                        tr_completeness    status )
1540{
1541    assert( tr_isTorrent( tor ) );
1542    assert( ( status == TR_LEECH )
1543         || ( status == TR_SEED )
1544         || ( status == TR_PARTIAL_SEED ) );
1545
1546    if( tor->completeness_func )
1547        tor->completeness_func( tor, status, tor->completeness_func_user_data );
1548}
1549
1550void
1551tr_torrentSetCompletenessCallback( tr_torrent                    * tor,
1552                                   tr_torrent_completeness_func    func,
1553                                   void                          * user_data )
1554{
1555    assert( tr_isTorrent( tor ) );
1556
1557    tor->completeness_func = func;
1558    tor->completeness_func_user_data = user_data;
1559}
1560
1561void
1562tr_torrentSetRatioLimitHitCallback( tr_torrent                     * tor,
1563                                    tr_torrent_ratio_limit_hit_func  func,
1564                                    void                           * user_data )
1565{
1566    assert( tr_isTorrent( tor ) );
1567
1568    tor->ratio_limit_hit_func = func;
1569    tor->ratio_limit_hit_func_user_data = user_data;
1570}
1571
1572void
1573tr_torrentClearCompletenessCallback( tr_torrent * torrent )
1574{
1575    tr_torrentSetCompletenessCallback( torrent, NULL, NULL );
1576}
1577
1578void
1579tr_torrentClearRatioLimitHitCallback( tr_torrent * torrent )
1580{
1581    tr_torrentSetRatioLimitHitCallback( torrent, NULL, NULL );
1582}
1583
1584void
1585tr_torrentRecheckCompleteness( tr_torrent * tor )
1586{
1587    tr_completeness completeness;
1588
1589    assert( tr_isTorrent( tor ) );
1590
1591    tr_torrentLock( tor );
1592
1593    completeness = tr_cpGetStatus( &tor->completion );
1594
1595    if( completeness != tor->completeness )
1596    {
1597        const int recentChange = tor->downloadedCur != 0;
1598
1599        if( recentChange )
1600        {
1601            tr_torinf( tor, _( "State changed from \"%1$s\" to \"%2$s\"" ),
1602                      getCompletionString( tor->completeness ),
1603                      getCompletionString( completeness ) );
1604        }
1605
1606        tor->completeness = completeness;
1607        tor->needsSeedRatioCheck = TRUE;
1608        tr_fdTorrentClose( tor->uniqueId );
1609
1610        /* if the torrent is a seed now,
1611         * and the files used to be in the incompleteDir,
1612         * then move them to the destination directory */
1613        if( tr_torrentIsSeed( tor ) && ( tor->currentDir == tor->incompleteDir ) )
1614            tr_torrentSetLocation( tor, tor->downloadDir, TRUE, NULL, NULL );
1615
1616        fireCompletenessChange( tor, completeness );
1617
1618        if( recentChange && ( completeness == TR_SEED ) )
1619        {
1620            tr_announcerTorrentCompleted( tor );
1621
1622            tor->doneDate = tor->anyDate = time( NULL );
1623        }
1624
1625        tr_torrentSetDirty( tor );
1626    }
1627
1628    tr_torrentUnlock( tor );
1629}
1630
1631/**
1632***  File priorities
1633**/
1634
1635void
1636tr_torrentInitFilePriority( tr_torrent *    tor,
1637                            tr_file_index_t fileIndex,
1638                            tr_priority_t   priority )
1639{
1640    tr_piece_index_t i;
1641    tr_file *        file;
1642
1643    assert( tr_isTorrent( tor ) );
1644    assert( fileIndex < tor->info.fileCount );
1645    assert( tr_isPriority( priority ) );
1646
1647    file = &tor->info.files[fileIndex];
1648    file->priority = priority;
1649    for( i = file->firstPiece; i <= file->lastPiece; ++i )
1650        tor->info.pieces[i].priority = calculatePiecePriority( tor, i, fileIndex );
1651}
1652
1653void
1654tr_torrentSetFilePriorities( tr_torrent *      tor,
1655                             tr_file_index_t * files,
1656                             tr_file_index_t   fileCount,
1657                             tr_priority_t     priority )
1658{
1659    tr_file_index_t i;
1660
1661    assert( tr_isTorrent( tor ) );
1662
1663    tr_torrentLock( tor );
1664
1665    for( i = 0; i < fileCount; ++i )
1666        tr_torrentInitFilePriority( tor, files[i], priority );
1667
1668    tr_torrentSetDirty( tor );
1669    tr_torrentUnlock( tor );
1670}
1671
1672tr_priority_t
1673tr_torrentGetFilePriority( const tr_torrent * tor,
1674                           tr_file_index_t    file )
1675{
1676    tr_priority_t ret;
1677
1678    assert( tr_isTorrent( tor ) );
1679
1680    tr_torrentLock( tor );
1681    assert( tor );
1682    assert( file < tor->info.fileCount );
1683    ret = tor->info.files[file].priority;
1684    tr_torrentUnlock( tor );
1685
1686    return ret;
1687}
1688
1689tr_priority_t*
1690tr_torrentGetFilePriorities( const tr_torrent * tor )
1691{
1692    tr_file_index_t i;
1693    tr_priority_t * p;
1694
1695    assert( tr_isTorrent( tor ) );
1696
1697    tr_torrentLock( tor );
1698    p = tr_new0( tr_priority_t, tor->info.fileCount );
1699    for( i = 0; i < tor->info.fileCount; ++i )
1700        p[i] = tor->info.files[i].priority;
1701    tr_torrentUnlock( tor );
1702
1703    return p;
1704}
1705
1706/**
1707***  File DND
1708**/
1709
1710int
1711tr_torrentGetFileDL( const tr_torrent * tor,
1712                     tr_file_index_t    file )
1713{
1714    int doDownload;
1715
1716    assert( tr_isTorrent( tor ) );
1717
1718    tr_torrentLock( tor );
1719
1720    assert( file < tor->info.fileCount );
1721    doDownload = !tor->info.files[file].dnd;
1722
1723    tr_torrentUnlock( tor );
1724    return doDownload != 0;
1725}
1726
1727static void
1728setFileDND( tr_torrent * tor, tr_file_index_t fileIndex, int doDownload )
1729{
1730    tr_file *        file;
1731    const int        dnd = !doDownload;
1732    tr_piece_index_t firstPiece, firstPieceDND;
1733    tr_piece_index_t lastPiece, lastPieceDND;
1734    tr_file_index_t  i;
1735
1736    assert( tr_isTorrent( tor ) );
1737
1738    file = &tor->info.files[fileIndex];
1739    file->dnd = dnd;
1740    firstPiece = file->firstPiece;
1741    lastPiece = file->lastPiece;
1742
1743    /* can't set the first piece to DND unless
1744       every file using that piece is DND */
1745    firstPieceDND = dnd;
1746    if( fileIndex > 0 )
1747    {
1748        for( i = fileIndex - 1; firstPieceDND; --i )
1749        {
1750            if( tor->info.files[i].lastPiece != firstPiece )
1751                break;
1752            firstPieceDND = tor->info.files[i].dnd;
1753            if( !i )
1754                break;
1755        }
1756    }
1757
1758    /* can't set the last piece to DND unless
1759       every file using that piece is DND */
1760    lastPieceDND = dnd;
1761    for( i = fileIndex + 1; lastPieceDND && i < tor->info.fileCount; ++i )
1762    {
1763        if( tor->info.files[i].firstPiece != lastPiece )
1764            break;
1765        lastPieceDND = tor->info.files[i].dnd;
1766    }
1767
1768    if( firstPiece == lastPiece )
1769    {
1770        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
1771    }
1772    else
1773    {
1774        tr_piece_index_t pp;
1775        tor->info.pieces[firstPiece].dnd = firstPieceDND;
1776        tor->info.pieces[lastPiece].dnd = lastPieceDND;
1777        for( pp = firstPiece + 1; pp < lastPiece; ++pp )
1778            tor->info.pieces[pp].dnd = dnd;
1779    }
1780}
1781
1782void
1783tr_torrentInitFileDLs( tr_torrent      * tor,
1784                       tr_file_index_t * files,
1785                       tr_file_index_t   fileCount,
1786                       tr_bool           doDownload )
1787{
1788    tr_file_index_t i;
1789
1790    assert( tr_isTorrent( tor ) );
1791
1792    tr_torrentLock( tor );
1793
1794    for( i=0; i<fileCount; ++i )
1795        setFileDND( tor, files[i], doDownload );
1796    tr_cpInvalidateDND( &tor->completion );
1797    tor->needsSeedRatioCheck = TRUE;
1798
1799    tr_torrentUnlock( tor );
1800}
1801
1802void
1803tr_torrentSetFileDLs( tr_torrent *      tor,
1804                      tr_file_index_t * files,
1805                      tr_file_index_t   fileCount,
1806                      tr_bool           doDownload )
1807{
1808    assert( tr_isTorrent( tor ) );
1809
1810    tr_torrentLock( tor );
1811    tr_torrentInitFileDLs( tor, files, fileCount, doDownload );
1812    tr_torrentSetDirty( tor );
1813    tr_torrentUnlock( tor );
1814}
1815
1816/***
1817****
1818***/
1819
1820tr_priority_t
1821tr_torrentGetPriority( const tr_torrent * tor )
1822{
1823    assert( tr_isTorrent( tor ) );
1824
1825    return tor->bandwidth->priority;
1826}
1827
1828void
1829tr_torrentSetPriority( tr_torrent * tor, tr_priority_t priority )
1830{
1831    assert( tr_isTorrent( tor ) );
1832    assert( tr_isPriority( priority ) );
1833
1834    if( tor->bandwidth->priority != priority )
1835    {
1836        tor->bandwidth->priority = priority;
1837
1838        tr_torrentSetDirty( tor );
1839    }
1840}
1841
1842/***
1843****
1844***/
1845
1846void
1847tr_torrentSetPeerLimit( tr_torrent * tor,
1848                        uint16_t     maxConnectedPeers )
1849{
1850    assert( tr_isTorrent( tor ) );
1851
1852    tor->maxConnectedPeers = maxConnectedPeers;
1853}
1854
1855uint16_t
1856tr_torrentGetPeerLimit( const tr_torrent * tor )
1857{
1858    assert( tr_isTorrent( tor ) );
1859
1860    return tor->maxConnectedPeers;
1861}
1862
1863/***
1864****
1865***/
1866
1867tr_block_index_t
1868_tr_block( const tr_torrent * tor,
1869           tr_piece_index_t   index,
1870           uint32_t           offset )
1871{
1872    tr_block_index_t ret;
1873
1874    assert( tr_isTorrent( tor ) );
1875
1876    ret = index;
1877    ret *= ( tor->info.pieceSize / tor->blockSize );
1878    ret += offset / tor->blockSize;
1879    return ret;
1880}
1881
1882tr_bool
1883tr_torrentReqIsValid( const tr_torrent * tor,
1884                      tr_piece_index_t   index,
1885                      uint32_t           offset,
1886                      uint32_t           length )
1887{
1888    int err = 0;
1889
1890    assert( tr_isTorrent( tor ) );
1891
1892    if( index >= tor->info.pieceCount )
1893        err = 1;
1894    else if( length < 1 )
1895        err = 2;
1896    else if( ( offset + length ) > tr_torPieceCountBytes( tor, index ) )
1897        err = 3;
1898    else if( length > MAX_BLOCK_SIZE )
1899        err = 4;
1900    else if( tr_pieceOffset( tor, index, offset, length ) > tor->info.totalSize )
1901        err = 5;
1902
1903    if( err ) tr_tordbg( tor, "index %lu offset %lu length %lu err %d\n",
1904                              (unsigned long)index,
1905                              (unsigned long)offset,
1906                              (unsigned long)length,
1907                              err );
1908
1909    return !err;
1910}
1911
1912uint64_t
1913tr_pieceOffset( const tr_torrent * tor,
1914                tr_piece_index_t   index,
1915                uint32_t           offset,
1916                uint32_t           length )
1917{
1918    uint64_t ret;
1919
1920    assert( tr_isTorrent( tor ) );
1921
1922    ret = tor->info.pieceSize;
1923    ret *= index;
1924    ret += offset;
1925    ret += length;
1926    return ret;
1927}
1928
1929/***
1930****
1931***/
1932
1933void
1934tr_torrentSetPieceChecked( tr_torrent        * tor,
1935                           tr_piece_index_t    piece,
1936                           tr_bool             isChecked )
1937{
1938    assert( tr_isTorrent( tor ) );
1939
1940    if( isChecked )
1941        tr_bitfieldAdd( &tor->checkedPieces, piece );
1942    else
1943        tr_bitfieldRem( &tor->checkedPieces, piece );
1944}
1945
1946void
1947tr_torrentSetFileChecked( tr_torrent *    tor,
1948                          tr_file_index_t fileIndex,
1949                          tr_bool         isChecked )
1950{
1951    const tr_file *        file = &tor->info.files[fileIndex];
1952    const tr_piece_index_t begin = file->firstPiece;
1953    const tr_piece_index_t end = file->lastPiece + 1;
1954
1955    assert( tr_isTorrent( tor ) );
1956
1957    if( isChecked )
1958        tr_bitfieldAddRange( &tor->checkedPieces, begin, end );
1959    else
1960        tr_bitfieldRemRange( &tor->checkedPieces, begin, end );
1961}
1962
1963tr_bool
1964tr_torrentIsFileChecked( const tr_torrent * tor,
1965                         tr_file_index_t    fileIndex )
1966{
1967    const tr_file *        file = &tor->info.files[fileIndex];
1968    const tr_piece_index_t begin = file->firstPiece;
1969    const tr_piece_index_t end = file->lastPiece + 1;
1970    tr_piece_index_t       i;
1971    tr_bool                isChecked = TRUE;
1972
1973    assert( tr_isTorrent( tor ) );
1974
1975    for( i = begin; isChecked && i < end; ++i )
1976        if( !tr_torrentIsPieceChecked( tor, i ) )
1977            isChecked = FALSE;
1978
1979    return isChecked;
1980}
1981
1982void
1983tr_torrentUncheck( tr_torrent * tor )
1984{
1985    assert( tr_isTorrent( tor ) );
1986
1987    tr_bitfieldRemRange( &tor->checkedPieces, 0, tor->info.pieceCount );
1988}
1989
1990int
1991tr_torrentCountUncheckedPieces( const tr_torrent * tor )
1992{
1993    assert( tr_isTorrent( tor ) );
1994
1995    return tor->info.pieceCount - tr_bitfieldCountTrueBits( &tor->checkedPieces );
1996}
1997
1998time_t*
1999tr_torrentGetMTimes( const tr_torrent * tor, size_t * setme_n )
2000{
2001    size_t       i;
2002    const size_t n = tor->info.fileCount;
2003    time_t *     m = tr_new0( time_t, n );
2004
2005    assert( tr_isTorrent( tor ) );
2006
2007    for( i = 0; i < n; ++i )
2008    {
2009        struct stat sb;
2010        char * path = tr_torrentFindFile( tor, i );
2011        if( ( path != NULL ) && !stat( path, &sb ) && S_ISREG( sb.st_mode ) )
2012        {
2013#ifdef SYS_DARWIN
2014            m[i] = sb.st_mtimespec.tv_sec;
2015#else
2016            m[i] = sb.st_mtime;
2017#endif
2018        }
2019        tr_free( path );
2020    }
2021
2022    *setme_n = n;
2023    return m;
2024}
2025
2026/***
2027****
2028***/
2029
2030tr_announce_list_err
2031tr_torrentSetAnnounceList( tr_torrent *            tor,
2032                           const tr_tracker_info * trackers,
2033                           int                     trackerCount )
2034{
2035    int i, j;
2036    tr_benc metainfo;
2037
2038    assert( tr_isTorrent( tor ) );
2039
2040    /* look for bad URLs */
2041    for( i=0; i<trackerCount; ++i )
2042        if( !tr_httpIsValidURL( trackers[i].announce ) )
2043            return TR_ANNOUNCE_LIST_HAS_BAD;
2044
2045    /* look for duplicates */
2046    for( i=0; i<trackerCount; ++i )
2047        for( j=0; j<trackerCount; ++j )
2048            if( ( i != j ) && ( !strcmp( trackers[i].announce, trackers[j].announce ) ) )
2049                return TR_ANNOUNCE_LIST_HAS_DUPLICATES;
2050
2051    /* save to the .torrent file */
2052    if( !tr_bencLoadFile( &metainfo, TR_FMT_BENC, tor->info.torrent ) )
2053    {
2054        int       i;
2055        int       prevTier = -1;
2056        tr_benc * tier = NULL;
2057        tr_benc * announceList;
2058        tr_info   tmpInfo;
2059
2060        /* remove the old fields */
2061        tr_bencDictRemove( &metainfo, "announce" );
2062        tr_bencDictRemove( &metainfo, "announce-list" );
2063
2064        /* add the new fields */
2065        tr_bencDictAddStr( &metainfo, "announce", trackers[0].announce );
2066        announceList = tr_bencDictAddList( &metainfo, "announce-list", 0 );
2067        for( i = 0; i < trackerCount; ++i ) {
2068            if( prevTier != trackers[i].tier ) {
2069                prevTier = trackers[i].tier;
2070                tier = tr_bencListAddList( announceList, 0 );
2071            }
2072            tr_bencListAddStr( tier, trackers[i].announce );
2073        }
2074
2075        /* try to parse it back again, to make sure it's good */
2076        memset( &tmpInfo, 0, sizeof( tr_info ) );
2077        if( tr_metainfoParse( tor->session, &tmpInfo, &metainfo ) )
2078        {
2079            /* it's good, so keep these new trackers and free the old ones */
2080
2081            tr_info swap;
2082            swap.trackers = tor->info.trackers;
2083            swap.trackerCount = tor->info.trackerCount;
2084            tor->info.trackers = tmpInfo.trackers;
2085            tor->info.trackerCount = tmpInfo.trackerCount;
2086            tmpInfo.trackers = swap.trackers;
2087            tmpInfo.trackerCount = swap.trackerCount;
2088
2089            tr_metainfoFree( &tmpInfo );
2090            tr_bencToFile( &metainfo, TR_FMT_BENC, tor->info.torrent );
2091        }
2092
2093        /* cleanup */
2094        tr_bencFree( &metainfo );
2095
2096        /* tell the announcer to reload this torrent's tracker list */
2097        tr_announcerResetTorrent( tor->session->announcer, tor );
2098    }
2099
2100    return TR_ANNOUNCE_LIST_OK;
2101}
2102
2103/**
2104***
2105**/
2106
2107void
2108tr_torrentSetAddedDate( tr_torrent * tor,
2109                        time_t       t )
2110{
2111    assert( tr_isTorrent( tor ) );
2112
2113    tor->addedDate = t;
2114    tor->anyDate = MAX( tor->anyDate, tor->addedDate );
2115}
2116
2117void
2118tr_torrentSetActivityDate( tr_torrent * tor, time_t t )
2119{
2120    assert( tr_isTorrent( tor ) );
2121
2122    tor->activityDate = t;
2123    tor->anyDate = MAX( tor->anyDate, tor->activityDate );
2124}
2125
2126void
2127tr_torrentSetDoneDate( tr_torrent * tor,
2128                       time_t       t )
2129{
2130    assert( tr_isTorrent( tor ) );
2131
2132    tor->doneDate = t;
2133    tor->anyDate = MAX( tor->anyDate, tor->doneDate );
2134}
2135
2136/**
2137***
2138**/
2139
2140uint64_t
2141tr_torrentGetBytesLeftToAllocate( const tr_torrent * tor )
2142{
2143    tr_file_index_t i;
2144    uint64_t bytesLeft = 0;
2145
2146    assert( tr_isTorrent( tor ) );
2147
2148    for( i=0; i<tor->info.fileCount; ++i )
2149    {
2150        if( !tor->info.files[i].dnd )
2151        {
2152            struct stat sb;
2153            const uint64_t length = tor->info.files[i].length;
2154            char * path = tr_torrentFindFile( tor, i );
2155
2156            bytesLeft += length;
2157
2158            if( ( path != NULL ) && !stat( path, &sb )
2159                                 && S_ISREG( sb.st_mode )
2160                                 && ( (uint64_t)sb.st_size <= length ) )
2161                bytesLeft -= sb.st_size;
2162
2163            tr_free( path );
2164        }
2165    }
2166
2167    return bytesLeft;
2168}
2169
2170/****
2171*****  Removing the torrent's local data
2172****/
2173
2174static int
2175vstrcmp( const void * a, const void * b )
2176{
2177    return strcmp( a, b );
2178}
2179
2180static int
2181compareLongestFirst( const void * a, const void * b )
2182{
2183    const size_t alen = strlen( a );
2184    const size_t blen = strlen( b );
2185
2186    if( alen != blen )
2187        return alen > blen ? -1 : 1;
2188
2189    return vstrcmp( a, b );
2190}
2191
2192static void
2193addDirtyFile( const char  * root,
2194              const char  * filename,
2195              tr_ptrArray * dirtyFolders )
2196{
2197    char * dir = tr_dirname( filename );
2198
2199    /* add the parent folders to dirtyFolders until we reach the root or a known-dirty */
2200    while (     ( dir != NULL )
2201             && ( strlen( root ) <= strlen( dir ) )
2202             && ( tr_ptrArrayFindSorted( dirtyFolders, dir, vstrcmp ) == NULL ) )
2203    {
2204        char * tmp;
2205        tr_ptrArrayInsertSorted( dirtyFolders, tr_strdup( dir ), vstrcmp );
2206
2207        tmp = tr_dirname( dir );
2208        tr_free( dir );
2209        dir = tmp;
2210    }
2211
2212    tr_free( dir );
2213}
2214
2215static void
2216walkLocalData( const tr_torrent * tor,
2217               const char       * root,
2218               const char       * dir,
2219               const char       * base,
2220               tr_ptrArray      * torrentFiles,
2221               tr_ptrArray      * folders,
2222               tr_ptrArray      * dirtyFolders )
2223{
2224    int i;
2225    struct stat sb;
2226    char * buf;
2227
2228    assert( tr_isTorrent( tor ) );
2229
2230    buf = tr_buildPath( dir, base, NULL );
2231    i = stat( buf, &sb );
2232    if( !i )
2233    {
2234        DIR * odir = NULL;
2235
2236        if( S_ISDIR( sb.st_mode ) && ( ( odir = opendir ( buf ) ) ) )
2237        {
2238            struct dirent *d;
2239            tr_ptrArrayInsertSorted( folders, tr_strdup( buf ), vstrcmp );
2240            for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
2241                if( d->d_name && d->d_name[0] != '.' ) /* skip dotfiles */
2242                    walkLocalData( tor, root, buf, d->d_name, torrentFiles, folders, dirtyFolders );
2243            closedir( odir );
2244        }
2245        else if( S_ISREG( sb.st_mode ) && ( sb.st_size > 0 ) )
2246        {
2247            const char * sub = buf + strlen( tor->currentDir ) + strlen( TR_PATH_DELIMITER_STR );
2248            const tr_bool isTorrentFile = tr_ptrArrayFindSorted( torrentFiles, sub, vstrcmp ) != NULL;
2249            if( !isTorrentFile )
2250                addDirtyFile( root, buf, dirtyFolders );
2251        }
2252    }
2253
2254    tr_free( buf );
2255}
2256
2257static void
2258deleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
2259{
2260    int i, n;
2261    char ** s;
2262    tr_file_index_t f;
2263    tr_ptrArray torrentFiles = TR_PTR_ARRAY_INIT;
2264    tr_ptrArray folders      = TR_PTR_ARRAY_INIT;
2265    tr_ptrArray dirtyFolders = TR_PTR_ARRAY_INIT; /* dirty == contains non-torrent files */
2266
2267    const char * firstFile = tor->info.files[0].name;
2268    const char * cpch = strchr( firstFile, TR_PATH_DELIMITER );
2269    char * tmp = cpch ? tr_strndup( firstFile, cpch - firstFile ) : NULL;
2270    char * root = tr_buildPath( tor->currentDir, tmp, NULL );
2271
2272    assert( tr_isTorrent( tor ) );
2273
2274    for( f=0; f<tor->info.fileCount; ++f ) {
2275        tr_ptrArrayInsertSorted( &torrentFiles, tr_strdup( tor->info.files[f].name ), vstrcmp );
2276        tr_ptrArrayInsertSorted( &torrentFiles, tr_torrentBuildPartial( tor, f ), vstrcmp );
2277    }
2278
2279    /* build the set of folders and dirtyFolders */
2280    walkLocalData( tor, root, root, NULL, &torrentFiles, &folders, &dirtyFolders );
2281
2282    /* try to remove entire folders first, so that the recycle bin will be tidy */
2283    s = (char**) tr_ptrArrayPeek( &folders, &n );
2284    for( i=0; i<n; ++i )
2285        if( tr_ptrArrayFindSorted( &dirtyFolders, s[i], vstrcmp ) == NULL )
2286            fileFunc( s[i] );
2287
2288    /* now blow away any remaining torrent files, such as torrent files in dirty folders */
2289    for( f=0; f<tor->info.fileCount; ++f ) {
2290        char * path = tr_buildPath( tor->currentDir, tor->info.files[f].name, NULL );
2291        fileFunc( path );
2292        tr_free( path );
2293    }
2294
2295    /* Now clean out the directories left empty from the previous step.
2296     * Work from deepest to shallowest s.t. lower folders
2297     * won't prevent the upper folders from being deleted */
2298    {
2299        tr_ptrArray cleanFolders = TR_PTR_ARRAY_INIT;
2300        s = (char**) tr_ptrArrayPeek( &folders, &n );
2301        for( i=0; i<n; ++i )
2302            if( tr_ptrArrayFindSorted( &dirtyFolders, s[i], vstrcmp ) == NULL )
2303                tr_ptrArrayInsertSorted( &cleanFolders, s[i], compareLongestFirst );
2304        s = (char**) tr_ptrArrayPeek( &cleanFolders, &n );
2305        for( i=0; i<n; ++i )
2306            fileFunc( s[i] );
2307        tr_ptrArrayDestruct( &cleanFolders, NULL );
2308    }
2309
2310    /* cleanup */
2311    tr_ptrArrayDestruct( &dirtyFolders, tr_free );
2312    tr_ptrArrayDestruct( &folders, tr_free );
2313    tr_ptrArrayDestruct( &torrentFiles, tr_free );
2314    tr_free( root );
2315    tr_free( tmp );
2316}
2317
2318void
2319tr_torrentDeleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
2320{
2321    assert( tr_isTorrent( tor ) );
2322
2323    if( fileFunc == NULL )
2324        fileFunc = remove;
2325
2326    /* close all the files because we're about to delete them */
2327    tr_fdTorrentClose( tor->uniqueId );
2328
2329    if( tor->info.fileCount > 1 )
2330        deleteLocalData( tor, fileFunc );
2331    else {
2332        /* torrent only has one file */
2333        char * path = tr_torrentFindFile( tor, 0 );
2334        fileFunc( path );
2335        tr_free( path );
2336    }
2337}
2338
2339/***
2340****
2341***/
2342
2343struct LocationData
2344{
2345    tr_bool move_from_old_location;
2346    int * setme_state;
2347    double * setme_progress;
2348    char * location;
2349    tr_torrent * tor;
2350};
2351
2352static void
2353setLocation( void * vdata )
2354{
2355    tr_bool err = FALSE;
2356    tr_bool verify_needed = FALSE;
2357    struct LocationData * data = vdata;
2358    tr_torrent * tor = data->tor;
2359    const tr_bool do_move = data->move_from_old_location;
2360    const char * location = data->location;
2361    double bytesHandled = 0;
2362
2363    assert( tr_isTorrent( tor ) );
2364
2365    if( strcmp( location, tor->currentDir ) )
2366    {
2367        tr_file_index_t i;
2368
2369        /* bad idea to move files while they're being verified... */
2370        tr_verifyRemove( tor );
2371
2372        /* if the torrent is running, stop it and set a flag to
2373         * restart after we're done */
2374        if( tor->isRunning )
2375        {
2376            tr_torrentStop( tor );
2377            tor->startAfterVerify = TRUE;
2378        }
2379
2380        /* try to move the files.
2381         * FIXME: there are still all kinds of nasty cases, like what
2382         * if the target directory runs out of space halfway through... */
2383        for( i=0; !err && i<tor->info.fileCount; ++i )
2384        {
2385            const tr_file * f = &tor->info.files[i];
2386            const char * oldbase;
2387            char * sub;
2388            if( tr_torrentFindFile2( tor, i, &oldbase, &sub ) )
2389            {
2390                char * oldpath = tr_buildPath( oldbase, sub, NULL );
2391                char * newpath = tr_buildPath( location, sub, NULL );
2392
2393                if( do_move )
2394                {
2395                    errno = 0;
2396                    tr_torinf( tor, "moving \"%s\" to \"%s\"", oldpath, newpath );
2397                    if( rename( oldpath, newpath ) )
2398                    {
2399                        verify_needed = TRUE;
2400                        if( tr_moveFile( oldpath, newpath ) )
2401                        {
2402                            err = TRUE;
2403                            tr_torerr( tor, "error moving \"%s\" to \"%s\": %s",
2404                                            oldpath, newpath, tr_strerror( errno ) );
2405                        }
2406                    }
2407                }
2408
2409                tr_free( newpath );
2410                tr_free( oldpath );
2411                tr_free( sub );
2412            }
2413
2414            if( data->setme_progress )
2415            {
2416                bytesHandled += f->length;
2417                *data->setme_progress = bytesHandled / tor->info.totalSize;
2418            }
2419        }
2420
2421        if( !err )
2422        {
2423            /* blow away the leftover subdirectories in the old location */
2424            if( do_move && verify_needed )
2425                tr_torrentDeleteLocalData( tor, remove );
2426
2427            /* set the new location and reverify */
2428            tr_torrentSetDownloadDir( tor, location );
2429            if( verify_needed )
2430                tr_torrentVerify( tor );
2431        }
2432    }
2433
2434    if( data->setme_state )
2435        *data->setme_state = err ? TR_LOC_ERROR : TR_LOC_DONE;
2436
2437    /* cleanup */
2438    tr_free( data->location );
2439    tr_free( data );
2440}
2441
2442void
2443tr_torrentSetLocation( tr_torrent  * tor,
2444                       const char  * location,
2445                       tr_bool       move_from_old_location,
2446                       double      * setme_progress,
2447                       int         * setme_state )
2448{
2449    struct LocationData * data;
2450
2451    assert( tr_isTorrent( tor ) );
2452
2453    if( setme_state )
2454        *setme_state = TR_LOC_MOVING;
2455    if( setme_progress )
2456        *setme_progress = 0;
2457
2458    /* run this in the libtransmission thread */
2459    data = tr_new( struct LocationData, 1 );
2460    data->tor = tor;
2461    data->location = tr_strdup( location );
2462    data->move_from_old_location = move_from_old_location;
2463    data->setme_state = setme_state;
2464    data->setme_progress = setme_progress;
2465    tr_runInEventThread( tor->session, setLocation, data );
2466}
2467
2468/***
2469****
2470***/
2471
2472void
2473tr_torrentCheckSeedRatio( tr_torrent * tor )
2474{
2475    double seedRatio;
2476
2477    assert( tr_isTorrent( tor ) );
2478
2479    /* if we're seeding and we've reached our seed ratio limit, stop the torrent */
2480    if( tor->isRunning && tr_torrentIsSeed( tor ) && tr_torrentGetSeedRatio( tor, &seedRatio ) )
2481    {
2482        const uint64_t up = tor->uploadedCur + tor->uploadedPrev;
2483        uint64_t down = tor->downloadedCur + tor->downloadedPrev;
2484        double ratio;
2485
2486        /* maybe we're the initial seeder and never downloaded anything... */
2487        if( down == 0 )
2488            down = tr_cpHaveValid( &tor->completion );
2489
2490        ratio = tr_getRatio( up, down );
2491
2492        if( ratio >= seedRatio || ratio == TR_RATIO_INF )
2493        {
2494            tr_torrentStop( tor );
2495
2496            /* set to no ratio limit to allow easy restarting */
2497            tr_torrentSetRatioMode( tor, TR_RATIOLIMIT_UNLIMITED );
2498
2499            /* maybe notify the client */
2500            if( tor->ratio_limit_hit_func != NULL )
2501                tor->ratio_limit_hit_func( tor, tor->ratio_limit_hit_func_user_data );
2502        }
2503    }
2504}
2505
2506/***
2507****
2508***/
2509
2510void
2511tr_torrentFileCompleted( tr_torrent * tor, tr_file_index_t fileNum )
2512{
2513    char * sub;
2514    const char * base;
2515
2516    /* close the file so that we can reopen in read-only mode as needed */
2517    tr_fdFileClose( tor, fileNum );
2518
2519    /* if the torrent's filename on disk isn't the same as the one in the metadata,
2520     * then it's been modified to denote that it was a partial file.
2521     * Now that it's complete, use the proper filename. */
2522    if( tr_torrentFindFile2( tor, fileNum, &base, &sub ) )
2523    {
2524        const tr_file * file = &tor->info.files[fileNum];
2525
2526        if( strcmp( sub, file->name ) )
2527        {
2528            char * oldpath = tr_buildPath( base, sub, NULL );
2529            char * newpath = tr_buildPath( base, file->name, NULL );
2530
2531            if( rename( oldpath, newpath ) )
2532                tr_torerr( tor, "Error moving \"%s\" to \"%s\": %s", oldpath, newpath, tr_strerror( errno ) );
2533
2534            tr_free( newpath );
2535            tr_free( oldpath );
2536        }
2537
2538        tr_free( sub );
2539    }
2540}
2541
2542/***
2543****
2544***/
2545
2546static tr_bool
2547fileExists( const char * filename )
2548{
2549    struct stat sb;
2550    const tr_bool ok = !stat( filename, &sb );
2551    return ok;
2552}
2553
2554tr_bool
2555tr_torrentFindFile2( const tr_torrent * tor, tr_file_index_t fileNum,
2556                     const char ** base, char ** subpath )
2557{
2558    char * part;
2559    const tr_file * file;
2560    const char * b = NULL;
2561    const char * s = NULL;
2562
2563    assert( tr_isTorrent( tor ) );
2564    assert( fileNum < tor->info.fileCount );
2565
2566    file = &tor->info.files[fileNum];
2567    part = tr_torrentBuildPartial( tor, fileNum );
2568
2569    if( b == NULL ) {
2570        char * filename = tr_buildPath( tor->downloadDir, part, NULL );
2571        if( fileExists( filename ) ) {
2572            b = tor->downloadDir;
2573            s = part;
2574        }
2575        tr_free( filename );
2576    }
2577
2578    if( b == NULL ) {
2579        char * filename = tr_buildPath( tor->downloadDir, file->name, NULL );
2580        if( fileExists( filename ) ) {
2581            b = tor->downloadDir;
2582            s = file->name;
2583        }
2584        tr_free( filename );
2585    }
2586
2587    if( tor->incompleteDir != NULL )
2588    {
2589        if( b == NULL ) {
2590            char * filename = tr_buildPath( tor->incompleteDir, part, NULL );
2591            if( fileExists( filename ) ) {
2592                b = tor->incompleteDir;
2593                s = part;
2594            }
2595            tr_free( filename );
2596        }
2597
2598        if( b == NULL ) {
2599            char * filename = tr_buildPath( tor->incompleteDir, file->name, NULL );
2600            if( fileExists( filename ) ) {
2601                b = tor->incompleteDir;
2602                s = file->name;
2603            }
2604            tr_free( filename );
2605        }
2606    }
2607
2608    if( base != NULL )
2609        *base = b;
2610    if( subpath != NULL )
2611        *subpath = tr_strdup( s );
2612
2613    tr_free( part );
2614    return b != NULL;
2615}
2616
2617
2618char*
2619tr_torrentFindFile( const tr_torrent * tor, tr_file_index_t fileNum )
2620{
2621    char * subpath;
2622    char * ret = NULL;
2623    const char * base;
2624
2625    if( tr_torrentFindFile2( tor, fileNum, &base, &subpath ) )
2626    {
2627        ret = tr_buildPath( base, subpath, NULL );
2628        tr_free( subpath );
2629    }
2630
2631    return ret;
2632}
2633
2634/* Decide whether we should be looking for files in downloadDir or incompleteDir. */
2635static void
2636refreshCurrentDir( tr_torrent * tor )
2637{
2638    const char * dir = NULL;
2639    char * sub;
2640
2641    if( tor->incompleteDir == NULL )
2642        dir = tor->downloadDir;
2643    else if( !tr_torrentFindFile2( tor, 0, &dir, &sub ) )
2644        dir = tor->incompleteDir;
2645
2646    assert( dir != NULL );
2647    assert( ( dir == tor->downloadDir ) || ( dir == tor->incompleteDir ) );
2648    tor->currentDir = dir;
2649}
2650
2651char*
2652tr_torrentBuildPartial( const tr_torrent * tor, tr_file_index_t fileNum )
2653{
2654    return tr_strdup_printf( "%s.part", tor->info.files[fileNum].name );
2655}
Note: See TracBrowser for help on using the repository browser.