source: trunk/libtransmission/torrent.c @ 12424

Last change on this file since 12424 was 12424, checked in by jordan, 11 years ago

(trunk libT) CPU improvement in torrent.c's torrentInit()

Add the new torrent to the session's torrent list by prepending it instead of appending it. That way we don't have to walk the list in order to add it. tr_session.torrentList is an unordered list, so there's no real difference between prepending and appending.

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