source: trunk/libtransmission/torrent.c @ 9396

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

when calculating the "smoothed" eta, 1. only factor in previous speeds when those speeds were recent (ie. if we just resumed from pause, discard the previous speed) and 2. factor in if the speed was 0 as well

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