source: trunk/libtransmission/torrent.c @ 12248

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

(trunk libT) break the mac build and introduce new crashes.

This is partially to address #4145 "Downloads stuck at 100%" by refactoring the bitset, bitfield, and tr_completion; however, the ripple effect is larger than usual so things may get worse in the short term before getting better.

livings124: to fix the mac build, remove bitset.[ch] from xcode

  • Property svn:keywords set to Date Rev Author Id
File size: 80.5 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 12248 2011-03-28 16:31:05Z 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    bool has_local_data = false;
766    const tr_file_index_t n = tor->info.fileCount;
767
768    for( i=0; i<n && !has_local_data; ++i )
769    {
770        struct stat sb;
771        char * filename = tr_torrentFindFile( tor, i );
772
773        if( filename && !stat( filename, &sb ) )
774            has_local_data = true;
775
776        tr_free( filename );
777    }
778
779    return has_local_data;
780}
781
782static bool
783setLocalErrorIfFilesDisappeared( tr_torrent * tor )
784{
785    const bool disappeared = ( tr_cpHaveTotal( &tor->completion ) > 0 ) && !hasAnyLocalData( tor );
786
787    if( disappeared )
788    {
789        tr_deeplog_tor( tor, "%s", "[LAZY] uh oh, the files disappeared" );
790        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." ) );
791    }
792
793    return disappeared;
794}
795
796static void
797torrentInit( tr_torrent * tor, const tr_ctor * ctor )
798{
799    int doStart;
800    uint64_t loaded;
801    const char * dir;
802    bool isNewTorrent;
803    struct stat st;
804    static int nextUniqueId = 1;
805    tr_session * session = tr_ctorGetSession( ctor );
806
807    assert( session != NULL );
808
809    tr_sessionLock( session );
810
811    tor->session   = session;
812    tor->uniqueId = nextUniqueId++;
813    tor->magicNumber = TORRENT_MAGIC_NUMBER;
814
815    tr_peerIdInit( tor->peer_id );
816
817    tr_sha1( tor->obfuscatedHash, "req2", 4,
818             tor->info.hash, SHA_DIGEST_LENGTH,
819             NULL );
820
821    if( !tr_ctorGetDownloadDir( ctor, TR_FORCE, &dir ) ||
822        !tr_ctorGetDownloadDir( ctor, TR_FALLBACK, &dir ) )
823            tor->downloadDir = tr_strdup( dir );
824
825    if( tr_ctorGetIncompleteDir( ctor, &dir ) )
826        dir = tr_sessionGetIncompleteDir( session );
827    if( tr_sessionIsIncompleteDirEnabled( session ) )
828        tor->incompleteDir = tr_strdup( dir );
829
830    tor->bandwidth = tr_bandwidthNew( session, session->bandwidth );
831
832    tor->bandwidth->priority = tr_ctorGetBandwidthPriority( ctor );
833
834    tor->error = TR_STAT_OK;
835
836    tor->finishedSeedingByIdle = false;
837
838    tr_peerMgrAddTorrent( session->peerMgr, tor );
839
840    assert( !tor->downloadedCur );
841    assert( !tor->uploadedCur );
842
843    tr_torrentSetAddedDate( tor, tr_time( ) ); /* this is a default value to be
844                                                  overwritten by the resume file */
845
846    torrentInitFromInfo( tor );
847    loaded = tr_torrentLoadResume( tor, ~0, ctor );
848    tor->completeness = tr_cpGetStatus( &tor->completion );
849    setLocalErrorIfFilesDisappeared( tor );
850
851    tr_ctorInitTorrentPriorities( ctor, tor );
852    tr_ctorInitTorrentWanted( ctor, tor );
853
854    refreshCurrentDir( tor );
855
856    doStart = tor->isRunning;
857    tor->isRunning = 0;
858
859    if( !( loaded & TR_FR_SPEEDLIMIT ) )
860    {
861        tr_torrentUseSpeedLimit( tor, TR_UP, false );
862        tr_torrentSetSpeedLimit_Bps( tor, TR_UP, tr_sessionGetSpeedLimit_Bps( tor->session, TR_UP ) );
863        tr_torrentUseSpeedLimit( tor, TR_DOWN, false );
864        tr_torrentSetSpeedLimit_Bps( tor, TR_DOWN, tr_sessionGetSpeedLimit_Bps( tor->session, TR_DOWN ) );
865        tr_torrentUseSessionLimits( tor, true );
866    }
867
868    if( !( loaded & TR_FR_RATIOLIMIT ) )
869    {
870        tr_torrentSetRatioMode( tor, TR_RATIOLIMIT_GLOBAL );
871        tr_torrentSetRatioLimit( tor, tr_sessionGetRatioLimit( tor->session ) );
872    }
873
874    if( !( loaded & TR_FR_IDLELIMIT ) )
875    {
876        tr_torrentSetIdleMode( tor, TR_IDLELIMIT_GLOBAL );
877        tr_torrentSetIdleLimit( tor, tr_sessionGetIdleLimit( tor->session ) );
878    }
879
880    {
881        tr_torrent * it = NULL;
882        tr_torrent * last = NULL;
883        while( ( it = tr_torrentNext( session, it ) ) )
884            last = it;
885
886        if( !last )
887            session->torrentList = tor;
888        else
889            last->next = tor;
890        ++session->torrentCount;
891    }
892
893    /* if we don't have a local .torrent file already, assume the torrent is new */
894    isNewTorrent = stat( tor->info.torrent, &st );
895
896    /* maybe save our own copy of the metainfo */
897    if( tr_ctorGetSave( ctor ) )
898    {
899        const tr_benc * val;
900        if( !tr_ctorGetMetainfo( ctor, &val ) )
901        {
902            const char * path = tor->info.torrent;
903            const int err = tr_bencToFile( val, TR_FMT_BENC, path );
904            if( err )
905                tr_torrentSetLocalError( tor, "Unable to save torrent file: %s", tr_strerror( err ) );
906            tr_sessionSetTorrentFile( tor->session, tor->info.hashString, path );
907        }
908    }
909
910    tor->tiers = tr_announcerAddTorrent( tor, onTrackerResponse, NULL );
911
912    if( isNewTorrent )
913    {
914        tor->startAfterVerify = doStart;
915        tr_torrentVerify( tor );
916    }
917    else if( doStart )
918    {
919        torrentStart( tor );
920    }
921
922    tr_sessionUnlock( session );
923}
924
925static tr_parse_result
926torrentParseImpl( const tr_ctor * ctor, tr_info * setmeInfo,
927                  bool * setmeHasInfo, int * dictLength )
928{
929    int             doFree;
930    bool            didParse;
931    bool            hasInfo = false;
932    tr_info         tmp;
933    const tr_benc * metainfo;
934    tr_session    * session = tr_ctorGetSession( ctor );
935    tr_parse_result result = TR_PARSE_OK;
936
937    if( setmeInfo == NULL )
938        setmeInfo = &tmp;
939    memset( setmeInfo, 0, sizeof( tr_info ) );
940
941    if( tr_ctorGetMetainfo( ctor, &metainfo ) )
942        return TR_PARSE_ERR;
943
944    didParse = tr_metainfoParse( session, metainfo, setmeInfo,
945                                 &hasInfo, dictLength );
946    doFree = didParse && ( setmeInfo == &tmp );
947
948    if( !didParse )
949        result = TR_PARSE_ERR;
950
951    if( didParse && hasInfo && !tr_getBlockSize( setmeInfo->pieceSize ) )
952        result = TR_PARSE_ERR;
953
954    if( didParse && session && tr_torrentExists( session, setmeInfo->hash ) )
955        result = TR_PARSE_DUPLICATE;
956
957    if( doFree )
958        tr_metainfoFree( setmeInfo );
959
960    if( setmeHasInfo != NULL )
961        *setmeHasInfo = hasInfo;
962
963    return result;
964}
965
966tr_parse_result
967tr_torrentParse( const tr_ctor * ctor, tr_info * setmeInfo )
968{
969    return torrentParseImpl( ctor, setmeInfo, NULL, NULL );
970}
971
972tr_torrent *
973tr_torrentNew( const tr_ctor * ctor, int * setmeError )
974{
975    int len;
976    bool hasInfo;
977    tr_info tmpInfo;
978    tr_parse_result r;
979    tr_torrent * tor = NULL;
980
981    assert( ctor != NULL );
982    assert( tr_isSession( tr_ctorGetSession( ctor ) ) );
983
984    r = torrentParseImpl( ctor, &tmpInfo, &hasInfo, &len );
985    if( r == TR_PARSE_OK )
986    {
987        tor = tr_new0( tr_torrent, 1 );
988        tor->info = tmpInfo;
989        if( hasInfo )
990            tor->infoDictLength = len;
991        torrentInit( tor, ctor );
992    }
993    else
994    {
995        if( r == TR_PARSE_DUPLICATE )
996            tr_metainfoFree( &tmpInfo );
997
998        if( setmeError )
999            *setmeError = r;
1000    }
1001
1002    return tor;
1003}
1004
1005/**
1006***
1007**/
1008
1009void
1010tr_torrentSetDownloadDir( tr_torrent * tor, const char * path )
1011{
1012    assert( tr_isTorrent( tor  ) );
1013
1014    if( !path || !tor->downloadDir || strcmp( path, tor->downloadDir ) )
1015    {
1016        tr_free( tor->downloadDir );
1017        tor->downloadDir = tr_strdup( path );
1018        tr_torrentSetDirty( tor );
1019    }
1020
1021    refreshCurrentDir( tor );
1022}
1023
1024const char*
1025tr_torrentGetDownloadDir( const tr_torrent * tor )
1026{
1027    assert( tr_isTorrent( tor  ) );
1028
1029    return tor->downloadDir;
1030}
1031
1032const char *
1033tr_torrentGetCurrentDir( const tr_torrent * tor )
1034{
1035    assert( tr_isTorrent( tor  ) );
1036
1037    return tor->currentDir;
1038}
1039
1040
1041void
1042tr_torrentChangeMyPort( tr_torrent * tor )
1043{
1044    assert( tr_isTorrent( tor  ) );
1045
1046    if( tor->isRunning )
1047        tr_announcerChangeMyPort( tor );
1048}
1049
1050static inline void
1051tr_torrentManualUpdateImpl( void * vtor )
1052{
1053    tr_torrent * tor = vtor;
1054
1055    assert( tr_isTorrent( tor  ) );
1056
1057    if( tor->isRunning )
1058        tr_announcerManualAnnounce( tor );
1059}
1060
1061void
1062tr_torrentManualUpdate( tr_torrent * tor )
1063{
1064    assert( tr_isTorrent( tor  ) );
1065
1066    tr_runInEventThread( tor->session, tr_torrentManualUpdateImpl, tor );
1067}
1068
1069bool
1070tr_torrentCanManualUpdate( const tr_torrent * tor )
1071{
1072    return ( tr_isTorrent( tor  ) )
1073        && ( tor->isRunning )
1074        && ( tr_announcerCanManualAnnounce( tor ) );
1075}
1076
1077const tr_info *
1078tr_torrentInfo( const tr_torrent * tor )
1079{
1080    return tr_isTorrent( tor ) ? &tor->info : NULL;
1081}
1082
1083const tr_stat *
1084tr_torrentStatCached( tr_torrent * tor )
1085{
1086    const time_t now = tr_time( );
1087
1088    return tr_isTorrent( tor ) && ( now == tor->lastStatTime )
1089         ? &tor->stats
1090         : tr_torrentStat( tor );
1091}
1092
1093void
1094tr_torrentSetVerifyState( tr_torrent * tor, tr_verify_state state )
1095{
1096    assert( tr_isTorrent( tor ) );
1097    assert( state==TR_VERIFY_NONE || state==TR_VERIFY_WAIT || state==TR_VERIFY_NOW );
1098
1099    tor->verifyState = state;
1100    tor->anyDate = tr_time( );
1101}
1102
1103tr_torrent_activity
1104tr_torrentGetActivity( tr_torrent * tor )
1105{
1106    assert( tr_isTorrent( tor ) );
1107
1108    tr_torrentRecheckCompleteness( tor );
1109
1110    if( tor->verifyState == TR_VERIFY_NOW )
1111        return TR_STATUS_CHECK;
1112    if( tor->verifyState == TR_VERIFY_WAIT )
1113        return TR_STATUS_CHECK_WAIT;
1114    if( !tor->isRunning )
1115        return TR_STATUS_STOPPED;
1116    if( tor->completeness == TR_LEECH )
1117        return TR_STATUS_DOWNLOAD;
1118
1119    return TR_STATUS_SEED;
1120}
1121
1122static double
1123getVerifyProgress( const tr_torrent * tor )
1124{
1125    tr_piece_index_t i, n;
1126    tr_piece_index_t checked = 0;
1127
1128    assert( tr_isTorrent( tor ) );
1129
1130    for( i=0, n=tor->info.pieceCount; i!=n; ++i )
1131        if( tor->info.pieces[i].timeChecked )
1132            ++checked;
1133
1134    return checked / (double)tor->info.pieceCount;
1135}
1136
1137const tr_stat *
1138tr_torrentStat( tr_torrent * tor )
1139{
1140    tr_stat *               s;
1141    uint64_t                now;
1142    uint64_t                seedRatioBytesLeft;
1143    uint64_t                seedRatioBytesGoal;
1144    bool                    seedRatioApplies;
1145    uint16_t                seedIdleMinutes;
1146
1147    if( !tor )
1148        return NULL;
1149
1150    assert( tr_isTorrent( tor ) );
1151    tr_torrentLock( tor );
1152
1153    tor->lastStatTime = tr_time( );
1154
1155    s = &tor->stats;
1156    s->id = tor->uniqueId;
1157    s->activity = tr_torrentGetActivity( tor );
1158    s->error = tor->error;
1159    memcpy( s->errorString, tor->errorString, sizeof( s->errorString ) );
1160
1161    s->manualAnnounceTime = tr_announcerNextManualAnnounce( tor );
1162
1163    tr_peerMgrTorrentStats( tor,
1164                            &s->peersConnected,
1165                            &s->webseedsSendingToUs,
1166                            &s->peersSendingToUs,
1167                            &s->peersGettingFromUs,
1168                            s->peersFrom );
1169
1170    now = tr_time_msec( );
1171    s->rawUploadSpeed_KBps     = toSpeedKBps( tr_bandwidthGetRawSpeed_Bps  ( tor->bandwidth, now, TR_UP ) );
1172    s->pieceUploadSpeed_KBps   = toSpeedKBps( tr_bandwidthGetPieceSpeed_Bps( tor->bandwidth, now, TR_UP ) );
1173    s->rawDownloadSpeed_KBps   = toSpeedKBps( tr_bandwidthGetRawSpeed_Bps  ( tor->bandwidth, now, TR_DOWN ) );
1174    s->pieceDownloadSpeed_KBps = toSpeedKBps( tr_bandwidthGetPieceSpeed_Bps( tor->bandwidth, now, TR_DOWN ) );
1175
1176    s->percentComplete = tr_cpPercentComplete ( &tor->completion );
1177    s->metadataPercentComplete = tr_torrentGetMetadataPercent( tor );
1178
1179    s->percentDone         = tr_cpPercentDone  ( &tor->completion );
1180    s->leftUntilDone       = tr_cpLeftUntilDone( &tor->completion );
1181    s->sizeWhenDone        = tr_cpSizeWhenDone ( &tor->completion );
1182    s->recheckProgress     = s->activity == TR_STATUS_CHECK ? getVerifyProgress( tor ) : 0;
1183    s->activityDate        = tor->activityDate;
1184    s->addedDate           = tor->addedDate;
1185    s->doneDate            = tor->doneDate;
1186    s->startDate           = tor->startDate;
1187    s->secondsSeeding      = tor->secondsSeeding;
1188    s->secondsDownloading  = tor->secondsDownloading;
1189
1190    if ((s->activity == TR_STATUS_DOWNLOAD || s->activity == TR_STATUS_SEED) && s->startDate != 0)
1191        s->idleSecs = difftime(tr_time(), MAX(s->startDate, s->activityDate));
1192    else
1193        s->idleSecs = -1;
1194
1195    s->corruptEver      = tor->corruptCur    + tor->corruptPrev;
1196    s->downloadedEver   = tor->downloadedCur + tor->downloadedPrev;
1197    s->uploadedEver     = tor->uploadedCur   + tor->uploadedPrev;
1198    s->haveValid        = tr_cpHaveValid( &tor->completion );
1199    s->haveUnchecked    = tr_cpHaveTotal( &tor->completion ) - s->haveValid;
1200    s->desiredAvailable = tr_peerMgrGetDesiredAvailable( tor );
1201
1202    s->ratio = tr_getRatio( s->uploadedEver,
1203                            s->downloadedEver ? s->downloadedEver : s->haveValid );
1204
1205    seedRatioApplies = tr_torrentGetSeedRatioBytes( tor, &seedRatioBytesLeft,
1206                                                         &seedRatioBytesGoal );
1207
1208    switch( s->activity )
1209    {
1210        /* etaXLSpeed exists because if we use the piece speed directly,
1211         * brief fluctuations cause the ETA to jump all over the place.
1212         * so, etaXLSpeed is a smoothed-out version of the piece speed
1213         * to dampen the effect of fluctuations */
1214
1215        case TR_STATUS_DOWNLOAD:
1216            if( ( tor->etaDLSpeedCalculatedAt + 800 ) < now ) {
1217                tor->etaDLSpeed_KBps = ( ( tor->etaDLSpeedCalculatedAt + 4000 ) < now )
1218                    ? s->pieceDownloadSpeed_KBps /* if no recent previous speed, no need to smooth */
1219                    : ((tor->etaDLSpeed_KBps*4.0) + s->pieceDownloadSpeed_KBps)/5.0; /* smooth across 5 readings */
1220                tor->etaDLSpeedCalculatedAt = now;
1221            }
1222
1223            if( s->leftUntilDone > s->desiredAvailable )
1224                s->eta = TR_ETA_NOT_AVAIL;
1225            else if( tor->etaDLSpeed_KBps < 1 )
1226                s->eta = TR_ETA_UNKNOWN;
1227            else
1228                s->eta = s->leftUntilDone / toSpeedBytes(tor->etaDLSpeed_KBps);
1229
1230            s->etaIdle = TR_ETA_NOT_AVAIL;
1231            break;
1232
1233        case TR_STATUS_SEED: {
1234            if( !seedRatioApplies )
1235                s->eta = TR_ETA_NOT_AVAIL;
1236            else {
1237                if( ( tor->etaULSpeedCalculatedAt + 800 ) < now ) {
1238                    tor->etaULSpeed_KBps = ( ( tor->etaULSpeedCalculatedAt + 4000 ) < now )
1239                        ? s->pieceUploadSpeed_KBps /* if no recent previous speed, no need to smooth */
1240                        : ((tor->etaULSpeed_KBps*4.0) + s->pieceUploadSpeed_KBps)/5.0; /* smooth across 5 readings */
1241                    tor->etaULSpeedCalculatedAt = now;
1242                }
1243                if( tor->etaULSpeed_KBps < 1 )
1244                    s->eta = TR_ETA_UNKNOWN;
1245                else
1246                    s->eta = seedRatioBytesLeft / toSpeedBytes(tor->etaULSpeed_KBps);
1247            }
1248
1249            if( tor->etaULSpeed_KBps < 1 && tr_torrentGetSeedIdle( tor, &seedIdleMinutes ) )
1250                s->etaIdle = seedIdleMinutes * 60 - s->idleSecs;
1251            else
1252                s->etaIdle = TR_ETA_NOT_AVAIL;
1253            break;
1254        }
1255
1256        default:
1257            s->eta = TR_ETA_NOT_AVAIL;
1258            s->etaIdle = TR_ETA_NOT_AVAIL;
1259            break;
1260    }
1261
1262    /* s->haveValid is here to make sure a torrent isn't marked 'finished'
1263     * when the user hits "uncheck all" prior to starting the torrent... */
1264    s->finished = tor->finishedSeedingByIdle || (seedRatioApplies && !seedRatioBytesLeft && s->haveValid);
1265
1266    if( !seedRatioApplies || s->finished )
1267        s->seedRatioPercentDone = 1;
1268    else if( !seedRatioBytesGoal ) /* impossible? safeguard for div by zero */
1269        s->seedRatioPercentDone = 0;
1270    else
1271        s->seedRatioPercentDone = (double)(seedRatioBytesGoal - seedRatioBytesLeft) / seedRatioBytesGoal;
1272
1273    tr_torrentUnlock( tor );
1274
1275    return s;
1276}
1277
1278/***
1279****
1280***/
1281
1282static uint64_t
1283fileBytesCompleted( const tr_torrent * tor, tr_file_index_t index )
1284{
1285    uint64_t total = 0;
1286    const tr_file * f = &tor->info.files[index];
1287
1288    if( f->length )
1289    {
1290        tr_block_index_t first;
1291        tr_block_index_t last;
1292        tr_torGetFileBlockRange( tor, index, &first, &last );
1293
1294        if( first == last )
1295        {
1296            if( tr_cpBlockIsComplete( &tor->completion, first ) )
1297                total = f->length;
1298        }
1299        else
1300        {
1301            /* the first block */
1302            if( tr_cpBlockIsComplete( &tor->completion, first ) )
1303                total += tor->blockSize - ( f->offset % tor->blockSize );
1304
1305            /* the middle blocks */
1306            if( first + 1 < last ) {
1307                uint64_t u = tr_bitfieldCountRange( &tor->completion.blockBitfield, first+1, last );
1308                u *= tor->blockSize;
1309                total += u;
1310            }
1311
1312            /* the last block */
1313            if( tr_cpBlockIsComplete( &tor->completion, last ) )
1314                total += ( f->offset + f->length ) - ( (uint64_t)tor->blockSize * last );
1315        }
1316    }
1317
1318    return total;
1319}
1320
1321tr_file_stat *
1322tr_torrentFiles( const tr_torrent * tor,
1323                 tr_file_index_t *  fileCount )
1324{
1325    tr_file_index_t       i;
1326    const tr_file_index_t n = tor->info.fileCount;
1327    tr_file_stat *        files = tr_new0( tr_file_stat, n );
1328    tr_file_stat *        walk = files;
1329    const bool            isSeed = tor->completeness == TR_SEED;
1330
1331    assert( tr_isTorrent( tor ) );
1332
1333    for( i=0; i<n; ++i, ++walk ) {
1334        const uint64_t b = isSeed ? tor->info.files[i].length : fileBytesCompleted( tor, i );
1335        walk->bytesCompleted = b;
1336        walk->progress = tor->info.files[i].length > 0 ? ( (float)b / tor->info.files[i].length ) : 1.0f;
1337    }
1338
1339    if( fileCount )
1340        *fileCount = n;
1341
1342    return files;
1343}
1344
1345void
1346tr_torrentFilesFree( tr_file_stat *            files,
1347                     tr_file_index_t fileCount UNUSED )
1348{
1349    tr_free( files );
1350}
1351
1352/***
1353****
1354***/
1355
1356double*
1357tr_torrentWebSpeeds_KBps( const tr_torrent * tor )
1358{
1359    double * ret = NULL;
1360
1361    if( tr_isTorrent( tor ) )
1362    {
1363        tr_torrentLock( tor );
1364        ret = tr_peerMgrWebSpeeds_KBps( tor );
1365        tr_torrentUnlock( tor );
1366    }
1367
1368    return ret;
1369}
1370
1371tr_peer_stat *
1372tr_torrentPeers( const tr_torrent * tor, int * peerCount )
1373{
1374    tr_peer_stat * ret = NULL;
1375
1376    if( tr_isTorrent( tor ) )
1377    {
1378        tr_torrentLock( tor );
1379        ret = tr_peerMgrPeerStats( tor, peerCount );
1380        tr_torrentUnlock( tor );
1381    }
1382
1383    return ret;
1384}
1385
1386void
1387tr_torrentPeersFree( tr_peer_stat * peers, int peerCount UNUSED )
1388{
1389    tr_free( peers );
1390}
1391
1392tr_tracker_stat *
1393tr_torrentTrackers( const tr_torrent * torrent, int * setmeTrackerCount )
1394{
1395    tr_tracker_stat * ret = NULL;
1396
1397    if( tr_isTorrent( torrent ) )
1398    {
1399        tr_torrentLock( torrent );
1400        ret = tr_announcerStats( torrent, setmeTrackerCount );
1401        tr_torrentUnlock( torrent );
1402    }
1403
1404    return ret;
1405}
1406
1407void
1408tr_torrentTrackersFree( tr_tracker_stat * trackers, int trackerCount )
1409{
1410    tr_announcerStatsFree( trackers, trackerCount );
1411}
1412
1413void
1414tr_torrentAvailability( const tr_torrent * tor, int8_t * tab, int size )
1415{
1416    if( tr_isTorrent( tor ) && ( tab != NULL ) && ( size > 0 ) )
1417    {
1418        tr_torrentLock( tor );
1419        tr_peerMgrTorrentAvailability( tor, tab, size );
1420        tr_torrentUnlock( tor );
1421    }
1422}
1423
1424void
1425tr_torrentAmountFinished( const tr_torrent * tor,
1426                          float *            tab,
1427                          int                size )
1428{
1429    assert( tr_isTorrent( tor ) );
1430
1431    tr_torrentLock( tor );
1432    tr_cpGetAmountDone( &tor->completion, tab, size );
1433    tr_torrentUnlock( tor );
1434}
1435
1436static void
1437tr_torrentResetTransferStats( tr_torrent * tor )
1438{
1439    tr_torrentLock( tor );
1440
1441    tor->downloadedPrev += tor->downloadedCur;
1442    tor->downloadedCur   = 0;
1443    tor->uploadedPrev   += tor->uploadedCur;
1444    tor->uploadedCur     = 0;
1445    tor->corruptPrev    += tor->corruptCur;
1446    tor->corruptCur      = 0;
1447
1448    tr_torrentSetDirty( tor );
1449
1450    tr_torrentUnlock( tor );
1451}
1452
1453void
1454tr_torrentSetHasPiece( tr_torrent *     tor,
1455                       tr_piece_index_t pieceIndex,
1456                       bool             has )
1457{
1458    assert( tr_isTorrent( tor ) );
1459    assert( pieceIndex < tor->info.pieceCount );
1460
1461    if( has )
1462        tr_cpPieceAdd( &tor->completion, pieceIndex );
1463    else
1464        tr_cpPieceRem( &tor->completion, pieceIndex );
1465}
1466
1467/***
1468****
1469***/
1470
1471static void
1472freeTorrent( tr_torrent * tor )
1473{
1474    tr_torrent * t;
1475    tr_session *  session = tor->session;
1476    tr_info *    inf = &tor->info;
1477
1478    assert( !tor->isRunning );
1479
1480    tr_sessionLock( session );
1481
1482    tr_peerMgrRemoveTorrent( tor );
1483
1484    tr_cpDestruct( &tor->completion );
1485
1486    tr_announcerRemoveTorrent( session->announcer, tor );
1487
1488    tr_free( tor->downloadDir );
1489    tr_free( tor->incompleteDir );
1490
1491    if( tor == session->torrentList )
1492        session->torrentList = tor->next;
1493    else for( t = session->torrentList; t != NULL; t = t->next ) {
1494        if( t->next == tor ) {
1495            t->next = tor->next;
1496            break;
1497        }
1498    }
1499
1500    assert( session->torrentCount >= 1 );
1501    session->torrentCount--;
1502
1503    tr_bandwidthFree( tor->bandwidth );
1504
1505    tr_metainfoFree( inf );
1506    tr_free( tor );
1507
1508    tr_sessionUnlock( session );
1509}
1510
1511/**
1512***  Start/Stop Callback
1513**/
1514
1515static void
1516torrentStartImpl( void * vtor )
1517{
1518    time_t now;
1519    tr_torrent * tor = vtor;
1520
1521    assert( tr_isTorrent( tor ) );
1522
1523    tr_sessionLock( tor->session );
1524
1525    tr_torrentRecheckCompleteness( tor );
1526
1527    now = tr_time( );
1528    tor->isRunning = true;
1529    tor->completeness = tr_cpGetStatus( &tor->completion );
1530    tor->startDate = tor->anyDate = now;
1531    tr_torrentClearError( tor );
1532    tor->finishedSeedingByIdle = false;
1533
1534    tr_torrentResetTransferStats( tor );
1535    tr_announcerTorrentStarted( tor );
1536    tor->dhtAnnounceAt = now + tr_cryptoWeakRandInt( 20 );
1537    tor->dhtAnnounce6At = now + tr_cryptoWeakRandInt( 20 );
1538    tor->lpdAnnounceAt = now;
1539    tr_peerMgrStartTorrent( tor );
1540
1541    tr_sessionUnlock( tor->session );
1542}
1543
1544uint64_t
1545tr_torrentGetCurrentSizeOnDisk( const tr_torrent * tor )
1546{
1547    tr_file_index_t i;
1548    uint64_t byte_count = 0;
1549    const tr_file_index_t n = tor->info.fileCount;
1550
1551    for( i=0; i<n; ++i )
1552    {
1553        struct stat sb;
1554        char * filename = tr_torrentFindFile( tor, i );
1555
1556        sb.st_size = 0;
1557        if( filename && !stat( filename, &sb ) )
1558            byte_count += sb.st_size;
1559
1560        tr_free( filename );
1561    }
1562
1563    return byte_count;
1564}
1565
1566static void
1567torrentStart( tr_torrent * tor )
1568{
1569    /* already running... */
1570    if( tor->isRunning )
1571        return;
1572
1573    /* don't allow the torrent to be started if the files disappeared */
1574    if( setLocalErrorIfFilesDisappeared( tor ) )
1575        return;
1576
1577    /* verifying right now... wait until that's done so
1578     * we'll know what completeness to use/announce */
1579    if( tor->verifyState != TR_VERIFY_NONE ) {
1580        tor->startAfterVerify = true;
1581        return;
1582    }
1583
1584    /* otherwise, start it now... */
1585    tr_sessionLock( tor->session );
1586
1587    /* allow finished torrents to be resumed */
1588    if( tr_torrentIsSeedRatioDone( tor ) ) {
1589        tr_torinf( tor, _( "Restarted manually -- disabling its seed ratio" ) );
1590        tr_torrentSetRatioMode( tor, TR_RATIOLIMIT_UNLIMITED );
1591    }
1592
1593    /* corresponds to the peer_id sent as a tracker request parameter.
1594     * one tracker admin says: "When the same torrent is opened and
1595     * closed and opened again without quitting Transmission ...
1596     * change the peerid. It would help sometimes if a stopped event
1597     * was missed to ensure that we didn't think someone was cheating. */
1598    tr_peerIdInit( tor->peer_id );
1599    tor->isRunning = 1;
1600    tr_torrentSetDirty( tor );
1601    tr_runInEventThread( tor->session, torrentStartImpl, tor );
1602
1603    tr_sessionUnlock( tor->session );
1604}
1605
1606void
1607tr_torrentStart( tr_torrent * tor )
1608{
1609    if( tr_isTorrent( tor ) )
1610        torrentStart( tor );
1611}
1612
1613static void
1614torrentRecheckDoneImpl( void * vtor )
1615{
1616    tr_torrent * tor = vtor;
1617    assert( tr_isTorrent( tor ) );
1618
1619    tr_torrentRecheckCompleteness( tor );
1620
1621    if( tor->startAfterVerify ) {
1622        tor->startAfterVerify = false;
1623        torrentStart( tor );
1624    }
1625}
1626
1627static void
1628torrentRecheckDoneCB( tr_torrent * tor )
1629{
1630    assert( tr_isTorrent( tor ) );
1631
1632    tr_runInEventThread( tor->session, torrentRecheckDoneImpl, tor );
1633}
1634
1635static void
1636verifyTorrent( void * vtor )
1637{
1638    tr_torrent * tor = vtor;
1639
1640    tr_sessionLock( tor->session );
1641
1642    /* if the torrent's already being verified, stop it */
1643    tr_verifyRemove( tor );
1644
1645    /* if the torrent's running, stop it & set the restart-after-verify flag */
1646    if( tor->startAfterVerify || tor->isRunning ) {
1647        /* don't clobber isStopping */
1648        const bool startAfter = tor->isStopping ? false : true;
1649        tr_torrentStop( tor );
1650        tor->startAfterVerify = startAfter;
1651    }
1652
1653    if( setLocalErrorIfFilesDisappeared( tor ) )
1654        tor->startAfterVerify = false;
1655    else
1656        tr_verifyAdd( tor, torrentRecheckDoneCB );
1657
1658    tr_sessionUnlock( tor->session );
1659}
1660
1661void
1662tr_torrentVerify( tr_torrent * tor )
1663{
1664    if( tr_isTorrent( tor ) )
1665        tr_runInEventThread( tor->session, verifyTorrent, tor );
1666}
1667
1668void
1669tr_torrentSave( tr_torrent * tor )
1670{
1671    assert( tr_isTorrent( tor ) );
1672
1673    if( tor->isDirty )
1674    {
1675        tor->isDirty = false;
1676        tr_torrentSaveResume( tor );
1677    }
1678}
1679
1680static void
1681stopTorrent( void * vtor )
1682{
1683    tr_torrent * tor = vtor;
1684    tr_torinf( tor, "Pausing" );
1685
1686    assert( tr_isTorrent( tor ) );
1687
1688    tr_torrentLock( tor );
1689
1690    tr_verifyRemove( tor );
1691    tr_peerMgrStopTorrent( tor );
1692    tr_announcerTorrentStopped( tor );
1693    tr_cacheFlushTorrent( tor->session->cache, tor );
1694
1695    tr_fdTorrentClose( tor->session, tor->uniqueId );
1696
1697    if( !tor->isDeleting )
1698        tr_torrentSave( tor );
1699
1700    tr_torrentUnlock( tor );
1701}
1702
1703void
1704tr_torrentStop( tr_torrent * tor )
1705{
1706    assert( tr_isTorrent( tor ) );
1707
1708    if( tr_isTorrent( tor ) )
1709    {
1710        tr_sessionLock( tor->session );
1711
1712        tor->isRunning = 0;
1713        tor->isStopping = 0;
1714        tr_torrentSetDirty( tor );
1715        tr_runInEventThread( tor->session, stopTorrent, tor );
1716
1717        tr_sessionUnlock( tor->session );
1718    }
1719}
1720
1721static void
1722closeTorrent( void * vtor )
1723{
1724    tr_benc * d;
1725    tr_torrent * tor = vtor;
1726
1727    assert( tr_isTorrent( tor ) );
1728
1729    d = tr_bencListAddDict( &tor->session->removedTorrents, 2 );
1730    tr_bencDictAddInt( d, "id", tor->uniqueId );
1731    tr_bencDictAddInt( d, "date", tr_time( ) );
1732
1733    tr_torinf( tor, "%s", _( "Removing torrent" ) );
1734
1735    stopTorrent( tor );
1736
1737    if( tor->isDeleting )
1738    {
1739        tr_metainfoRemoveSaved( tor->session, &tor->info );
1740        tr_torrentRemoveResume( tor );
1741    }
1742
1743    tor->isRunning = 0;
1744    freeTorrent( tor );
1745}
1746
1747void
1748tr_torrentFree( tr_torrent * tor )
1749{
1750    if( tr_isTorrent( tor ) )
1751    {
1752        tr_session * session = tor->session;
1753        assert( tr_isSession( session ) );
1754        tr_sessionLock( session );
1755
1756        tr_torrentClearCompletenessCallback( tor );
1757        tr_runInEventThread( session, closeTorrent, tor );
1758
1759        tr_sessionUnlock( session );
1760    }
1761}
1762
1763struct remove_data
1764{
1765    tr_torrent   * tor;
1766    bool           deleteFlag;
1767    tr_fileFunc  * deleteFunc;
1768};
1769
1770static void tr_torrentDeleteLocalData( tr_torrent *, tr_fileFunc );
1771
1772static void
1773removeTorrent( void * vdata )
1774{
1775    struct remove_data * data = vdata;
1776
1777    if( data->deleteFlag )
1778        tr_torrentDeleteLocalData( data->tor, data->deleteFunc );
1779
1780    tr_torrentClearCompletenessCallback( data->tor );
1781    closeTorrent( data->tor );
1782    tr_free( data );
1783}
1784
1785void
1786tr_torrentRemove( tr_torrent   * tor,
1787                  bool           deleteFlag,
1788                  tr_fileFunc    deleteFunc )
1789{
1790    struct remove_data * data;
1791
1792    assert( tr_isTorrent( tor ) );
1793    tor->isDeleting = 1;
1794
1795    data = tr_new0( struct remove_data, 1 );
1796    data->tor = tor;
1797    data->deleteFlag = deleteFlag;
1798    data->deleteFunc = deleteFunc;
1799    tr_runInEventThread( tor->session, removeTorrent, data );
1800}
1801
1802/**
1803***  Completeness
1804**/
1805
1806static const char *
1807getCompletionString( int type )
1808{
1809    switch( type )
1810    {
1811        /* Translators: this is a minor point that's safe to skip over, but FYI:
1812           "Complete" and "Done" are specific, different terms in Transmission:
1813           "Complete" means we've downloaded every file in the torrent.
1814           "Done" means we're done downloading the files we wanted, but NOT all
1815           that exist */
1816        case TR_PARTIAL_SEED:
1817            return _( "Done" );
1818
1819        case TR_SEED:
1820            return _( "Complete" );
1821
1822        default:
1823            return _( "Incomplete" );
1824    }
1825}
1826
1827static void
1828fireCompletenessChange( tr_torrent       * tor,
1829                        tr_completeness    status,
1830                        bool               wasRunning )
1831{
1832    assert( ( status == TR_LEECH )
1833         || ( status == TR_SEED )
1834         || ( status == TR_PARTIAL_SEED ) );
1835
1836    if( tor->completeness_func )
1837        tor->completeness_func( tor, status, wasRunning,
1838                                tor->completeness_func_user_data );
1839}
1840
1841void
1842tr_torrentSetCompletenessCallback( tr_torrent                    * tor,
1843                                   tr_torrent_completeness_func    func,
1844                                   void                          * user_data )
1845{
1846    assert( tr_isTorrent( tor ) );
1847
1848    tor->completeness_func = func;
1849    tor->completeness_func_user_data = user_data;
1850}
1851
1852void
1853tr_torrentClearCompletenessCallback( tr_torrent * torrent )
1854{
1855    tr_torrentSetCompletenessCallback( torrent, NULL, NULL );
1856}
1857
1858void
1859tr_torrentSetRatioLimitHitCallback( tr_torrent                     * tor,
1860                                    tr_torrent_ratio_limit_hit_func  func,
1861                                    void                           * user_data )
1862{
1863    assert( tr_isTorrent( tor ) );
1864
1865    tor->ratio_limit_hit_func = func;
1866    tor->ratio_limit_hit_func_user_data = user_data;
1867}
1868
1869void
1870tr_torrentClearRatioLimitHitCallback( tr_torrent * torrent )
1871{
1872    tr_torrentSetRatioLimitHitCallback( torrent, NULL, NULL );
1873}
1874
1875void
1876tr_torrentSetIdleLimitHitCallback( tr_torrent                    * tor,
1877                                   tr_torrent_idle_limit_hit_func  func,
1878                                   void                          * user_data )
1879{
1880    assert( tr_isTorrent( tor ) );
1881
1882    tor->idle_limit_hit_func = func;
1883    tor->idle_limit_hit_func_user_data = user_data;
1884}
1885
1886void
1887tr_torrentClearIdleLimitHitCallback( tr_torrent * torrent )
1888{
1889    tr_torrentSetIdleLimitHitCallback( torrent, NULL, NULL );
1890}
1891
1892static void
1893onSigCHLD( int i UNUSED )
1894{
1895    waitpid( -1, NULL, WNOHANG );
1896}
1897
1898static void
1899torrentCallScript( const tr_torrent * tor, const char * script )
1900{
1901    char timeStr[128];
1902    const time_t now = tr_time( );
1903
1904    tr_strlcpy( timeStr, ctime( &now ), sizeof( timeStr ) );
1905    *strchr( timeStr,'\n' ) = '\0';
1906
1907    if( script && *script )
1908    {
1909        int i;
1910        char * cmd[] = { tr_strdup( script ), NULL };
1911        char * env[] = {
1912            tr_strdup_printf( "TR_APP_VERSION=%s", SHORT_VERSION_STRING ),
1913            tr_strdup_printf( "TR_TIME_LOCALTIME=%s", timeStr ),
1914            tr_strdup_printf( "TR_TORRENT_DIR=%s", tor->currentDir ),
1915            tr_strdup_printf( "TR_TORRENT_ID=%d", tr_torrentId( tor ) ),
1916            tr_strdup_printf( "TR_TORRENT_HASH=%s", tor->info.hashString ),
1917            tr_strdup_printf( "TR_TORRENT_NAME=%s", tr_torrentName( tor ) ),
1918            NULL };
1919
1920        tr_torinf( tor, "Calling script \"%s\"", script );
1921
1922#ifdef WIN32
1923        _spawnvpe( _P_NOWAIT, script, (const char*)cmd, env );
1924#else
1925        signal( SIGCHLD, onSigCHLD );
1926
1927        if( !fork( ) )
1928        {
1929            for (i=0; env[i]; ++i)
1930                putenv(env[i]);
1931            execvp( script, cmd );
1932            _exit( 0 );
1933        }
1934#endif
1935
1936        for( i=0; cmd[i]; ++i ) tr_free( cmd[i] );
1937        for( i=0; env[i]; ++i ) tr_free( env[i] );
1938    }
1939}
1940
1941void
1942tr_torrentRecheckCompleteness( tr_torrent * tor )
1943{
1944    tr_completeness completeness;
1945
1946    assert( tr_isTorrent( tor ) );
1947
1948    tr_torrentLock( tor );
1949
1950    completeness = tr_cpGetStatus( &tor->completion );
1951
1952    if( completeness != tor->completeness )
1953    {
1954        const int recentChange = tor->downloadedCur != 0;
1955        const bool wasLeeching = !tr_torrentIsSeed( tor );
1956        const bool wasRunning = tor->isRunning;
1957
1958        if( recentChange )
1959        {
1960            tr_torinf( tor, _( "State changed from \"%1$s\" to \"%2$s\"" ),
1961                      getCompletionString( tor->completeness ),
1962                      getCompletionString( completeness ) );
1963        }
1964
1965        tor->completeness = completeness;
1966        tr_fdTorrentClose( tor->session, tor->uniqueId );
1967
1968        if( tr_torrentIsSeed( tor ) )
1969        {
1970            if( recentChange )
1971            {
1972                tr_announcerTorrentCompleted( tor );
1973                tor->doneDate = tor->anyDate = tr_time( );
1974            }
1975
1976            if( wasLeeching && wasRunning )
1977            {
1978                /* clear interested flag on all peers */
1979                tr_peerMgrClearInterest( tor );
1980
1981                /* if completeness was TR_LEECH then the seed limit check will have been skipped in bandwidthPulse */
1982                tr_torrentCheckSeedLimit( tor );
1983            }
1984
1985            if( tor->currentDir == tor->incompleteDir )
1986                tr_torrentSetLocation( tor, tor->downloadDir, true, NULL, NULL );
1987
1988            if( tr_sessionIsTorrentDoneScriptEnabled( tor->session ) )
1989                torrentCallScript( tor, tr_sessionGetTorrentDoneScript( tor->session ) );
1990        }
1991
1992        fireCompletenessChange( tor, wasRunning, completeness );
1993
1994        tr_torrentSetDirty( tor );
1995    }
1996
1997    tr_torrentUnlock( tor );
1998}
1999
2000/***
2001****
2002***/
2003
2004static void
2005tr_torrentFireMetadataCompleted( tr_torrent * tor )
2006{
2007    assert( tr_isTorrent( tor ) );
2008
2009    if( tor->metadata_func )
2010        tor->metadata_func( tor, tor->metadata_func_user_data );
2011}
2012
2013void
2014tr_torrentSetMetadataCallback( tr_torrent                * tor,
2015                               tr_torrent_metadata_func    func,
2016                               void                      * user_data )
2017{
2018    assert( tr_isTorrent( tor ) );
2019
2020    tor->metadata_func = func;
2021    tor->metadata_func_user_data = user_data;
2022}
2023
2024
2025/**
2026***  File priorities
2027**/
2028
2029void
2030tr_torrentInitFilePriority( tr_torrent *    tor,
2031                            tr_file_index_t fileIndex,
2032                            tr_priority_t   priority )
2033{
2034    tr_piece_index_t i;
2035    tr_file *        file;
2036
2037    assert( tr_isTorrent( tor ) );
2038    assert( fileIndex < tor->info.fileCount );
2039    assert( tr_isPriority( priority ) );
2040
2041    file = &tor->info.files[fileIndex];
2042    file->priority = priority;
2043    for( i = file->firstPiece; i <= file->lastPiece; ++i )
2044        tor->info.pieces[i].priority = calculatePiecePriority( tor, i, fileIndex );
2045}
2046
2047void
2048tr_torrentSetFilePriorities( tr_torrent             * tor,
2049                             const tr_file_index_t  * files,
2050                             tr_file_index_t          fileCount,
2051                             tr_priority_t            priority )
2052{
2053    tr_file_index_t i;
2054    assert( tr_isTorrent( tor ) );
2055    tr_torrentLock( tor );
2056
2057    for( i = 0; i < fileCount; ++i )
2058        if( files[i] < tor->info.fileCount )
2059            tr_torrentInitFilePriority( tor, files[i], priority );
2060    tr_torrentSetDirty( tor );
2061    tr_peerMgrRebuildRequests( tor );
2062
2063    tr_torrentUnlock( tor );
2064}
2065
2066tr_priority_t*
2067tr_torrentGetFilePriorities( const tr_torrent * tor )
2068{
2069    tr_file_index_t i;
2070    tr_priority_t * p;
2071
2072    assert( tr_isTorrent( tor ) );
2073
2074    tr_torrentLock( tor );
2075    p = tr_new0( tr_priority_t, tor->info.fileCount );
2076    for( i = 0; i < tor->info.fileCount; ++i )
2077        p[i] = tor->info.files[i].priority;
2078    tr_torrentUnlock( tor );
2079
2080    return p;
2081}
2082
2083/**
2084***  File DND
2085**/
2086
2087static void
2088setFileDND( tr_torrent * tor, tr_file_index_t fileIndex, int doDownload )
2089{
2090    const int8_t     dnd = !doDownload;
2091    tr_piece_index_t firstPiece;
2092    int8_t           firstPieceDND;
2093    tr_piece_index_t lastPiece;
2094    int8_t           lastPieceDND;
2095    tr_file_index_t  i;
2096    tr_file *        file = &tor->info.files[fileIndex];
2097
2098    file->dnd = dnd;
2099    firstPiece = file->firstPiece;
2100    lastPiece = file->lastPiece;
2101
2102    /* can't set the first piece to DND unless
2103       every file using that piece is DND */
2104    firstPieceDND = dnd;
2105    if( fileIndex > 0 )
2106    {
2107        for( i = fileIndex - 1; firstPieceDND; --i )
2108        {
2109            if( tor->info.files[i].lastPiece != firstPiece )
2110                break;
2111            firstPieceDND = tor->info.files[i].dnd;
2112            if( !i )
2113                break;
2114        }
2115    }
2116
2117    /* can't set the last piece to DND unless
2118       every file using that piece is DND */
2119    lastPieceDND = dnd;
2120    for( i = fileIndex + 1; lastPieceDND && i < tor->info.fileCount; ++i )
2121    {
2122        if( tor->info.files[i].firstPiece != lastPiece )
2123            break;
2124        lastPieceDND = tor->info.files[i].dnd;
2125    }
2126
2127    if( firstPiece == lastPiece )
2128    {
2129        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
2130    }
2131    else
2132    {
2133        tr_piece_index_t pp;
2134        tor->info.pieces[firstPiece].dnd = firstPieceDND;
2135        tor->info.pieces[lastPiece].dnd = lastPieceDND;
2136        for( pp = firstPiece + 1; pp < lastPiece; ++pp )
2137            tor->info.pieces[pp].dnd = dnd;
2138    }
2139}
2140
2141void
2142tr_torrentInitFileDLs( tr_torrent             * tor,
2143                       const tr_file_index_t  * files,
2144                       tr_file_index_t          fileCount,
2145                       bool                     doDownload )
2146{
2147    tr_file_index_t i;
2148
2149    assert( tr_isTorrent( tor ) );
2150
2151    tr_torrentLock( tor );
2152
2153    for( i=0; i<fileCount; ++i )
2154        if( files[i] < tor->info.fileCount )
2155            setFileDND( tor, files[i], doDownload );
2156
2157    tr_cpInvalidateDND( &tor->completion );
2158
2159    tr_torrentUnlock( tor );
2160}
2161
2162void
2163tr_torrentSetFileDLs( tr_torrent             * tor,
2164                      const tr_file_index_t  * files,
2165                      tr_file_index_t          fileCount,
2166                      bool                     doDownload )
2167{
2168    assert( tr_isTorrent( tor ) );
2169    tr_torrentLock( tor );
2170
2171    tr_torrentInitFileDLs( tor, files, fileCount, doDownload );
2172    tr_torrentSetDirty( tor );
2173    tr_peerMgrRebuildRequests( tor );
2174
2175    tr_torrentUnlock( tor );
2176}
2177
2178/***
2179****
2180***/
2181
2182tr_priority_t
2183tr_torrentGetPriority( const tr_torrent * tor )
2184{
2185    assert( tr_isTorrent( tor ) );
2186
2187    return tor->bandwidth->priority;
2188}
2189
2190void
2191tr_torrentSetPriority( tr_torrent * tor, tr_priority_t priority )
2192{
2193    assert( tr_isTorrent( tor ) );
2194    assert( tr_isPriority( priority ) );
2195
2196    if( tor->bandwidth->priority != priority )
2197    {
2198        tor->bandwidth->priority = priority;
2199
2200        tr_torrentSetDirty( tor );
2201    }
2202}
2203
2204/***
2205****
2206***/
2207
2208void
2209tr_torrentSetPeerLimit( tr_torrent * tor,
2210                        uint16_t     maxConnectedPeers )
2211{
2212    assert( tr_isTorrent( tor ) );
2213
2214    if ( tor->maxConnectedPeers != maxConnectedPeers )
2215    {
2216        tor->maxConnectedPeers = maxConnectedPeers;
2217
2218        tr_torrentSetDirty( tor );
2219    }
2220}
2221
2222uint16_t
2223tr_torrentGetPeerLimit( const tr_torrent * tor )
2224{
2225    assert( tr_isTorrent( tor ) );
2226
2227    return tor->maxConnectedPeers;
2228}
2229
2230/***
2231****
2232***/
2233
2234void
2235tr_torrentGetBlockLocation( const tr_torrent * tor,
2236                            tr_block_index_t   block,
2237                            tr_piece_index_t * piece,
2238                            uint32_t         * offset,
2239                            uint32_t         * length )
2240{
2241    uint64_t pos = block;
2242    pos *= tor->blockSize;
2243    *piece = pos / tor->info.pieceSize;
2244    *offset = pos - ( *piece * tor->info.pieceSize );
2245    *length = tr_torBlockCountBytes( tor, block );
2246}
2247
2248
2249tr_block_index_t
2250_tr_block( const tr_torrent * tor,
2251           tr_piece_index_t   index,
2252           uint32_t           offset )
2253{
2254    tr_block_index_t ret;
2255
2256    assert( tr_isTorrent( tor ) );
2257
2258    ret = index;
2259    ret *= ( tor->info.pieceSize / tor->blockSize );
2260    ret += offset / tor->blockSize;
2261    return ret;
2262}
2263
2264bool
2265tr_torrentReqIsValid( const tr_torrent * tor,
2266                      tr_piece_index_t   index,
2267                      uint32_t           offset,
2268                      uint32_t           length )
2269{
2270    int err = 0;
2271
2272    assert( tr_isTorrent( tor ) );
2273
2274    if( index >= tor->info.pieceCount )
2275        err = 1;
2276    else if( length < 1 )
2277        err = 2;
2278    else if( ( offset + length ) > tr_torPieceCountBytes( tor, index ) )
2279        err = 3;
2280    else if( length > MAX_BLOCK_SIZE )
2281        err = 4;
2282    else if( tr_pieceOffset( tor, index, offset, length ) > tor->info.totalSize )
2283        err = 5;
2284
2285    if( err ) tr_tordbg( tor, "index %lu offset %lu length %lu err %d\n",
2286                              (unsigned long)index,
2287                              (unsigned long)offset,
2288                              (unsigned long)length,
2289                              err );
2290
2291    return !err;
2292}
2293
2294uint64_t
2295tr_pieceOffset( const tr_torrent * tor,
2296                tr_piece_index_t   index,
2297                uint32_t           offset,
2298                uint32_t           length )
2299{
2300    uint64_t ret;
2301
2302    assert( tr_isTorrent( tor ) );
2303
2304    ret = tor->info.pieceSize;
2305    ret *= index;
2306    ret += offset;
2307    ret += length;
2308    return ret;
2309}
2310
2311void
2312tr_torGetFileBlockRange( const tr_torrent        * tor,
2313                         const tr_file_index_t     file,
2314                         tr_block_index_t        * first,
2315                         tr_block_index_t        * last )
2316{
2317    const tr_file * f = &tor->info.files[file];
2318    uint64_t offset = f->offset;
2319    *first = offset / tor->blockSize;
2320    if( !f->length )
2321        *last = *first;
2322    else {
2323        offset += f->length - 1;
2324        *last = offset / tor->blockSize;
2325    }
2326}
2327
2328void
2329tr_torGetPieceBlockRange( const tr_torrent        * tor,
2330                          const tr_piece_index_t    piece,
2331                          tr_block_index_t        * first,
2332                          tr_block_index_t        * last )
2333{
2334    uint64_t offset = tor->info.pieceSize;
2335    offset *= piece;
2336    *first = offset / tor->blockSize;
2337    offset += ( tr_torPieceCountBytes( tor, piece ) - 1 );
2338    *last = offset / tor->blockSize;
2339}
2340
2341
2342/***
2343****
2344***/
2345
2346void
2347tr_torrentSetPieceChecked( tr_torrent * tor, tr_piece_index_t pieceIndex )
2348{
2349    assert( tr_isTorrent( tor ) );
2350    assert( pieceIndex < tor->info.pieceCount );
2351
2352    tor->info.pieces[pieceIndex].timeChecked = tr_time( );
2353}
2354
2355void
2356tr_torrentSetChecked( tr_torrent * tor, time_t when )
2357{
2358    tr_piece_index_t i, n;
2359
2360    assert( tr_isTorrent( tor ) );
2361
2362    for( i=0, n=tor->info.pieceCount; i!=n; ++i )
2363        tor->info.pieces[i].timeChecked = when;
2364}
2365
2366bool
2367tr_torrentCheckPiece( tr_torrent * tor, tr_piece_index_t pieceIndex )
2368{
2369    const bool pass = tr_ioTestPiece( tor, pieceIndex );
2370
2371    tr_deeplog_tor( tor, "[LAZY] tr_torrentCheckPiece tested piece %zu, pass==%d", (size_t)pieceIndex, (int)pass );
2372    tr_torrentSetHasPiece( tor, pieceIndex, pass );
2373    tr_torrentSetPieceChecked( tor, pieceIndex );
2374    tor->anyDate = tr_time( );
2375    tr_torrentSetDirty( tor );
2376
2377    return pass;
2378}
2379
2380time_t
2381tr_torrentGetFileMTime( const tr_torrent * tor, tr_file_index_t i )
2382{
2383    struct stat sb;
2384    time_t mtime = 0;
2385    char * path = tr_torrentFindFile( tor, i );
2386
2387    if( ( path != NULL ) && !stat( path, &sb ) && S_ISREG( sb.st_mode ) )
2388    {
2389#ifdef SYS_DARWIN
2390        mtime = sb.st_mtimespec.tv_sec;
2391#else
2392        mtime = sb.st_mtime;
2393#endif
2394    }
2395
2396    tr_free( path );
2397    return mtime;
2398}
2399
2400bool
2401tr_torrentPieceNeedsCheck( const tr_torrent * tor, tr_piece_index_t p )
2402{
2403    uint64_t unused;
2404    tr_file_index_t f;
2405    const tr_info * inf = tr_torrentInfo( tor );
2406
2407    /* if we've never checked this piece, then it needs to be checked */
2408    if( !inf->pieces[p].timeChecked )
2409        return true;
2410
2411    /* If we think we've completed one of the files in this piece,
2412     * but it's been modified since we last checked it,
2413     * then it needs to be rechecked */
2414    tr_ioFindFileLocation( tor, p, 0, &f, &unused );
2415    for( ; f < inf->fileCount && pieceHasFile( p, &inf->files[f] ); ++f )
2416        if( tr_cpFileIsComplete( &tor->completion, f ) )
2417            if( tr_torrentGetFileMTime( tor, f ) > inf->pieces[p].timeChecked )
2418                return true;
2419
2420    return false;
2421}
2422
2423/***
2424****
2425***/
2426
2427static int
2428compareTrackerByTier( const void * va, const void * vb )
2429{
2430    const tr_tracker_info * a = va;
2431    const tr_tracker_info * b = vb;
2432
2433    /* sort by tier */
2434    if( a->tier != b->tier )
2435        return a->tier - b->tier;
2436
2437    /* get the effects of a stable sort by comparing the two elements' addresses */
2438    return a - b;
2439}
2440
2441bool
2442tr_torrentSetAnnounceList( tr_torrent             * tor,
2443                           const tr_tracker_info  * trackers_in,
2444                           int                      trackerCount )
2445{
2446    int i;
2447    tr_benc metainfo;
2448    bool ok = true;
2449    tr_tracker_info * trackers;
2450
2451    tr_torrentLock( tor );
2452
2453    assert( tr_isTorrent( tor ) );
2454
2455    /* ensure the trackers' tiers are in ascending order */
2456    trackers = tr_memdup( trackers_in, sizeof( tr_tracker_info ) * trackerCount );
2457    qsort( trackers, trackerCount, sizeof( tr_tracker_info ), compareTrackerByTier );
2458
2459    /* look for bad URLs */
2460    for( i=0; ok && i<trackerCount; ++i )
2461        if( !tr_urlIsValidTracker( trackers[i].announce ) )
2462            ok = false;
2463
2464    /* save to the .torrent file */
2465    if( ok && !tr_bencLoadFile( &metainfo, TR_FMT_BENC, tor->info.torrent ) )
2466    {
2467        bool hasInfo;
2468        tr_info tmpInfo;
2469
2470        /* remove the old fields */
2471        tr_bencDictRemove( &metainfo, "announce" );
2472        tr_bencDictRemove( &metainfo, "announce-list" );
2473
2474        /* add the new fields */
2475        if( trackerCount > 0 )
2476        {
2477            tr_bencDictAddStr( &metainfo, "announce", trackers[0].announce );
2478        }
2479        if( trackerCount > 1 )
2480        {
2481            int i;
2482            int prevTier = -1;
2483            tr_benc * tier = NULL;
2484            tr_benc * announceList = tr_bencDictAddList( &metainfo, "announce-list", 0 );
2485
2486            for( i=0; i<trackerCount; ++i ) {
2487                if( prevTier != trackers[i].tier ) {
2488                    prevTier = trackers[i].tier;
2489                    tier = tr_bencListAddList( announceList, 0 );
2490                }
2491                tr_bencListAddStr( tier, trackers[i].announce );
2492            }
2493        }
2494
2495        /* try to parse it back again, to make sure it's good */
2496        memset( &tmpInfo, 0, sizeof( tr_info ) );
2497        if( tr_metainfoParse( tor->session, &metainfo, &tmpInfo,
2498                              &hasInfo, &tor->infoDictLength ) )
2499        {
2500            /* it's good, so keep these new trackers and free the old ones */
2501
2502            tr_info swap;
2503            swap.trackers = tor->info.trackers;
2504            swap.trackerCount = tor->info.trackerCount;
2505            tor->info.trackers = tmpInfo.trackers;
2506            tor->info.trackerCount = tmpInfo.trackerCount;
2507            tmpInfo.trackers = swap.trackers;
2508            tmpInfo.trackerCount = swap.trackerCount;
2509
2510            tr_metainfoFree( &tmpInfo );
2511            tr_bencToFile( &metainfo, TR_FMT_BENC, tor->info.torrent );
2512        }
2513
2514        /* cleanup */
2515        tr_bencFree( &metainfo );
2516
2517        /* if we had a tracker-related error on this torrent,
2518         * and that tracker's been removed,
2519         * then clear the error */
2520        if(    ( tor->error == TR_STAT_TRACKER_WARNING )
2521            || ( tor->error == TR_STAT_TRACKER_ERROR ) )
2522        {
2523            bool clear = true;
2524
2525            for( i=0; clear && i<trackerCount; ++i )
2526                if( !strcmp( trackers[i].announce, tor->errorTracker ) )
2527                    clear = false;
2528
2529            if( clear )
2530                tr_torrentClearError( tor );
2531        }
2532
2533        /* tell the announcer to reload this torrent's tracker list */
2534        tr_announcerResetTorrent( tor->session->announcer, tor );
2535    }
2536
2537    tr_torrentUnlock( tor );
2538
2539    tr_free( trackers );
2540    return ok;
2541}
2542
2543/**
2544***
2545**/
2546
2547void
2548tr_torrentSetAddedDate( tr_torrent * tor,
2549                        time_t       t )
2550{
2551    assert( tr_isTorrent( tor ) );
2552
2553    tor->addedDate = t;
2554    tor->anyDate = MAX( tor->anyDate, tor->addedDate );
2555}
2556
2557void
2558tr_torrentSetActivityDate( tr_torrent * tor, time_t t )
2559{
2560    assert( tr_isTorrent( tor ) );
2561
2562    tor->activityDate = t;
2563    tor->anyDate = MAX( tor->anyDate, tor->activityDate );
2564}
2565
2566void
2567tr_torrentSetDoneDate( tr_torrent * tor,
2568                       time_t       t )
2569{
2570    assert( tr_isTorrent( tor ) );
2571
2572    tor->doneDate = t;
2573    tor->anyDate = MAX( tor->anyDate, tor->doneDate );
2574}
2575
2576/**
2577***
2578**/
2579
2580uint64_t
2581tr_torrentGetBytesLeftToAllocate( const tr_torrent * tor )
2582{
2583    tr_file_index_t i;
2584    uint64_t bytesLeft = 0;
2585
2586    assert( tr_isTorrent( tor ) );
2587
2588    for( i=0; i<tor->info.fileCount; ++i )
2589    {
2590        if( !tor->info.files[i].dnd )
2591        {
2592            struct stat sb;
2593            const uint64_t length = tor->info.files[i].length;
2594            char * path = tr_torrentFindFile( tor, i );
2595
2596            bytesLeft += length;
2597
2598            if( ( path != NULL ) && !stat( path, &sb )
2599                                 && S_ISREG( sb.st_mode )
2600                                 && ( (uint64_t)sb.st_size <= length ) )
2601                bytesLeft -= sb.st_size;
2602
2603            tr_free( path );
2604        }
2605    }
2606
2607    return bytesLeft;
2608}
2609
2610/****
2611*****  Removing the torrent's local data
2612****/
2613
2614static int
2615vstrcmp( const void * a, const void * b )
2616{
2617    return strcmp( a, b );
2618}
2619
2620static int
2621compareLongestFirst( const void * a, const void * b )
2622{
2623    const size_t alen = strlen( a );
2624    const size_t blen = strlen( b );
2625
2626    if( alen != blen )
2627        return alen > blen ? -1 : 1;
2628
2629    return vstrcmp( a, b );
2630}
2631
2632static void
2633addDirtyFile( const char  * root,
2634              const char  * filename,
2635              tr_ptrArray * dirtyFolders )
2636{
2637    char * dir = tr_dirname( filename );
2638
2639    /* add the parent folders to dirtyFolders until we reach the root or a known-dirty */
2640    while (     ( dir != NULL )
2641             && ( strlen( root ) <= strlen( dir ) )
2642             && ( tr_ptrArrayFindSorted( dirtyFolders, dir, vstrcmp ) == NULL ) )
2643    {
2644        char * tmp;
2645        tr_ptrArrayInsertSorted( dirtyFolders, tr_strdup( dir ), vstrcmp );
2646
2647        tmp = tr_dirname( dir );
2648        tr_free( dir );
2649        dir = tmp;
2650    }
2651
2652    tr_free( dir );
2653}
2654
2655static void
2656walkLocalData( const tr_torrent * tor,
2657               const char       * root,
2658               const char       * dir,
2659               const char       * base,
2660               tr_ptrArray      * torrentFiles,
2661               tr_ptrArray      * folders,
2662               tr_ptrArray      * dirtyFolders )
2663{
2664    struct stat sb;
2665    char * buf = tr_buildPath( dir, base, NULL );
2666    int i = stat( buf, &sb );
2667
2668    if( !i )
2669    {
2670        DIR * odir = NULL;
2671
2672        if( S_ISDIR( sb.st_mode ) && ( ( odir = opendir ( buf ) ) ) )
2673        {
2674            struct dirent *d;
2675            tr_ptrArrayInsertSorted( folders, tr_strdup( buf ), vstrcmp );
2676            for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
2677                if( d->d_name && strcmp( d->d_name, "." ) && strcmp( d->d_name, ".." ) )
2678                    walkLocalData( tor, root, buf, d->d_name, torrentFiles, folders, dirtyFolders );
2679            closedir( odir );
2680        }
2681        else if( S_ISREG( sb.st_mode ) && ( sb.st_size > 0 ) )
2682        {
2683            const char * sub = buf + strlen( tor->currentDir ) + strlen( TR_PATH_DELIMITER_STR );
2684            const bool isTorrentFile = tr_ptrArrayFindSorted( torrentFiles, sub, vstrcmp ) != NULL;
2685            if( !isTorrentFile )
2686                addDirtyFile( root, buf, dirtyFolders );
2687        }
2688    }
2689
2690    tr_free( buf );
2691}
2692
2693static void
2694deleteLocalFile( const char * filename, tr_fileFunc fileFunc )
2695{
2696    struct stat sb;
2697    if( !stat( filename, &sb ) ) /* if file exists... */
2698        fileFunc( filename );
2699}
2700
2701static void
2702deleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
2703{
2704    int i, n;
2705    char ** s;
2706    tr_file_index_t f;
2707    tr_ptrArray torrentFiles = TR_PTR_ARRAY_INIT;
2708    tr_ptrArray folders      = TR_PTR_ARRAY_INIT;
2709    tr_ptrArray dirtyFolders = TR_PTR_ARRAY_INIT; /* dirty == contains non-torrent files */
2710
2711    const char * firstFile = tor->info.files[0].name;
2712    const char * cpch = strchr( firstFile, TR_PATH_DELIMITER );
2713    char * tmp = cpch ? tr_strndup( firstFile, cpch - firstFile ) : NULL;
2714    char * root = tr_buildPath( tor->currentDir, tmp, NULL );
2715
2716    for( f=0; f<tor->info.fileCount; ++f ) {
2717        tr_ptrArrayInsertSorted( &torrentFiles, tr_strdup( tor->info.files[f].name ), vstrcmp );
2718        tr_ptrArrayInsertSorted( &torrentFiles, tr_torrentBuildPartial( tor, f ), vstrcmp );
2719    }
2720
2721    /* build the set of folders and dirtyFolders */
2722    walkLocalData( tor, root, root, NULL, &torrentFiles, &folders, &dirtyFolders );
2723
2724    /* try to remove entire folders first, so that the recycle bin will be tidy */
2725    s = (char**) tr_ptrArrayPeek( &folders, &n );
2726    for( i=0; i<n; ++i )
2727        if( tr_ptrArrayFindSorted( &dirtyFolders, s[i], vstrcmp ) == NULL )
2728            deleteLocalFile( s[i], fileFunc );
2729
2730    /* now blow away any remaining torrent files, such as torrent files in dirty folders */
2731    for( i=0, n=tr_ptrArraySize( &torrentFiles ); i<n; ++i ) {
2732        char * path = tr_buildPath( tor->currentDir, tr_ptrArrayNth( &torrentFiles, i ), NULL );
2733        deleteLocalFile( path, fileFunc );
2734        tr_free( path );
2735    }
2736
2737    /* Now clean out the directories left empty from the previous step.
2738     * Work from deepest to shallowest s.t. lower folders
2739     * won't prevent the upper folders from being deleted */
2740    {
2741        tr_ptrArray cleanFolders = TR_PTR_ARRAY_INIT;
2742        s = (char**) tr_ptrArrayPeek( &folders, &n );
2743        for( i=0; i<n; ++i )
2744            if( tr_ptrArrayFindSorted( &dirtyFolders, s[i], vstrcmp ) == NULL )
2745                tr_ptrArrayInsertSorted( &cleanFolders, s[i], compareLongestFirst );
2746        s = (char**) tr_ptrArrayPeek( &cleanFolders, &n );
2747        for( i=0; i<n; ++i ) {
2748#ifdef SYS_DARWIN
2749            char * dsStore = tr_buildPath( s[i], ".DS_Store", NULL );
2750            deleteLocalFile( dsStore, fileFunc );
2751            tr_free( dsStore );
2752#endif
2753            deleteLocalFile( s[i], fileFunc );
2754        }
2755        tr_ptrArrayDestruct( &cleanFolders, NULL );
2756    }
2757
2758    /* cleanup */
2759    tr_ptrArrayDestruct( &dirtyFolders, tr_free );
2760    tr_ptrArrayDestruct( &folders, tr_free );
2761    tr_ptrArrayDestruct( &torrentFiles, tr_free );
2762    tr_free( root );
2763    tr_free( tmp );
2764}
2765
2766static void
2767tr_torrentDeleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
2768{
2769    assert( tr_isTorrent( tor ) );
2770
2771    if( fileFunc == NULL )
2772        fileFunc = remove;
2773
2774    /* close all the files because we're about to delete them */
2775    tr_cacheFlushTorrent( tor->session->cache, tor );
2776    tr_fdTorrentClose( tor->session, tor->uniqueId );
2777
2778    if( tor->info.fileCount > 1 )
2779    {
2780        deleteLocalData( tor, fileFunc );
2781    }
2782    else if( tor->info.fileCount == 1 )
2783    {
2784        char * tmp;
2785
2786        /* torrent only has one file */
2787        char * path = tr_buildPath( tor->currentDir, tor->info.files[0].name, NULL );
2788        deleteLocalFile( path, fileFunc );
2789        tr_free( path );
2790
2791        tmp = tr_torrentBuildPartial( tor, 0 );
2792        path = tr_buildPath( tor->currentDir, tmp, NULL );
2793        deleteLocalFile( path, fileFunc );
2794        tr_free( path );
2795        tr_free( tmp );
2796    }
2797}
2798
2799/***
2800****
2801***/
2802
2803struct LocationData
2804{
2805    bool move_from_old_location;
2806    volatile int * setme_state;
2807    volatile double * setme_progress;
2808    char * location;
2809    tr_torrent * tor;
2810};
2811
2812static void
2813setLocation( void * vdata )
2814{
2815    bool err = false;
2816    struct LocationData * data = vdata;
2817    tr_torrent * tor = data->tor;
2818    const bool do_move = data->move_from_old_location;
2819    const char * location = data->location;
2820    double bytesHandled = 0;
2821
2822    assert( tr_isTorrent( tor ) );
2823
2824    tr_dbg( "Moving \"%s\" location from currentDir \"%s\" to \"%s\"",
2825            tr_torrentName(tor), tor->currentDir, location );
2826
2827    tr_mkdirp( location, 0777 );
2828
2829    if( !tr_is_same_file( location, tor->currentDir ) )
2830    {
2831        tr_file_index_t i;
2832
2833        /* bad idea to move files while they're being verified... */
2834        tr_verifyRemove( tor );
2835
2836        /* try to move the files.
2837         * FIXME: there are still all kinds of nasty cases, like what
2838         * if the target directory runs out of space halfway through... */
2839        for( i=0; !err && i<tor->info.fileCount; ++i )
2840        {
2841            const tr_file * f = &tor->info.files[i];
2842            const char * oldbase;
2843            char * sub;
2844            if( tr_torrentFindFile2( tor, i, &oldbase, &sub ) )
2845            {
2846                char * oldpath = tr_buildPath( oldbase, sub, NULL );
2847                char * newpath = tr_buildPath( location, sub, NULL );
2848
2849                tr_dbg( "Found file #%d: %s", (int)i, oldpath );
2850
2851                if( do_move && !tr_is_same_file( oldpath, newpath ) )
2852                {
2853                    bool renamed = false;
2854                    errno = 0;
2855                    tr_torinf( tor, "moving \"%s\" to \"%s\"", oldpath, newpath );
2856                    if( tr_moveFile( oldpath, newpath, &renamed ) )
2857                    {
2858                        err = true;
2859                        tr_torerr( tor, "error moving \"%s\" to \"%s\": %s",
2860                                        oldpath, newpath, tr_strerror( errno ) );
2861                    }
2862                }
2863
2864                tr_free( newpath );
2865                tr_free( oldpath );
2866                tr_free( sub );
2867            }
2868
2869            if( data->setme_progress )
2870            {
2871                bytesHandled += f->length;
2872                *data->setme_progress = bytesHandled / tor->info.totalSize;
2873            }
2874        }
2875
2876        if( !err )
2877        {
2878            /* blow away the leftover subdirectories in the old location */
2879            if( do_move )
2880                tr_torrentDeleteLocalData( tor, remove );
2881
2882            /* set the new location and reverify */
2883            tr_torrentSetDownloadDir( tor, location );
2884        }
2885    }
2886
2887    if( !err && do_move )
2888    {
2889        tr_free( tor->incompleteDir );
2890        tor->incompleteDir = NULL;
2891        tor->currentDir = tor->downloadDir;
2892    }
2893
2894    if( data->setme_state )
2895        *data->setme_state = err ? TR_LOC_ERROR : TR_LOC_DONE;
2896
2897    /* cleanup */
2898    tr_free( data->location );
2899    tr_free( data );
2900}
2901
2902void
2903tr_torrentSetLocation( tr_torrent       * tor,
2904                       const char       * location,
2905                       bool               move_from_old_location,
2906                       volatile double  * setme_progress,
2907                       volatile int     * setme_state )
2908{
2909    struct LocationData * data;
2910
2911    assert( tr_isTorrent( tor ) );
2912
2913    if( setme_state )
2914        *setme_state = TR_LOC_MOVING;
2915    if( setme_progress )
2916        *setme_progress = 0;
2917
2918    /* run this in the libtransmission thread */
2919    data = tr_new( struct LocationData, 1 );
2920    data->tor = tor;
2921    data->location = tr_strdup( location );
2922    data->move_from_old_location = move_from_old_location;
2923    data->setme_state = setme_state;
2924    data->setme_progress = setme_progress;
2925    tr_runInEventThread( tor->session, setLocation, data );
2926}
2927
2928/***
2929****
2930***/
2931
2932void
2933tr_torrentFileCompleted( tr_torrent * tor, tr_file_index_t fileNum )
2934{
2935    char * sub;
2936    const char * base;
2937    const tr_info * inf = &tor->info;
2938    const tr_file * f = &inf->files[fileNum];
2939    tr_piece * p;
2940    const tr_piece * pend;
2941    const time_t now = tr_time( );
2942
2943    /* close the file so that we can reopen in read-only mode as needed */
2944    tr_fdFileClose( tor->session, tor, fileNum );
2945
2946    /* now that the file is complete and closed, we can start watching its
2947     * mtime timestamp for changes to know if we need to reverify pieces */
2948    for( p=&inf->pieces[f->firstPiece], pend=&inf->pieces[f->lastPiece]; p!=pend; ++p )
2949        p->timeChecked = now;
2950
2951    /* if the torrent's current filename isn't the same as the one in the
2952     * metadata -- for example, if it had the ".part" suffix appended to
2953     * it until now -- then rename it to match the one in the metadata */
2954    if( tr_torrentFindFile2( tor, fileNum, &base, &sub ) )
2955    {
2956        if( strcmp( sub, f->name ) )
2957        {
2958            char * oldpath = tr_buildPath( base, sub, NULL );
2959            char * newpath = tr_buildPath( base, f->name, NULL );
2960
2961            if( rename( oldpath, newpath ) )
2962                tr_torerr( tor, "Error moving \"%s\" to \"%s\": %s", oldpath, newpath, tr_strerror( errno ) );
2963
2964            tr_free( newpath );
2965            tr_free( oldpath );
2966        }
2967
2968        tr_free( sub );
2969    }
2970}
2971
2972/***
2973****
2974***/
2975
2976static bool
2977fileExists( const char * filename )
2978{
2979    struct stat sb;
2980    const bool ok = !stat( filename, &sb );
2981    return ok;
2982}
2983
2984bool
2985tr_torrentFindFile2( const tr_torrent * tor, tr_file_index_t fileNum,
2986                     const char ** base, char ** subpath )
2987{
2988    char * part;
2989    const tr_file * file;
2990    const char * b = NULL;
2991    const char * s = NULL;
2992
2993    assert( tr_isTorrent( tor ) );
2994    assert( fileNum < tor->info.fileCount );
2995
2996    file = &tor->info.files[fileNum];
2997    part = tr_torrentBuildPartial( tor, fileNum );
2998
2999    if( b == NULL ) {
3000        char * filename = tr_buildPath( tor->downloadDir, file->name, NULL );
3001        if( fileExists( filename ) ) {
3002            b = tor->downloadDir;
3003            s = file->name;
3004        }
3005        tr_free( filename );
3006    }
3007
3008    if( ( b == NULL ) && ( tor->incompleteDir != NULL ) ) {
3009        char * filename = tr_buildPath( tor->incompleteDir, file->name, NULL );
3010        if( fileExists( filename ) ) {
3011            b = tor->incompleteDir;
3012            s = file->name;
3013        }
3014        tr_free( filename );
3015    }
3016
3017    if( ( b == NULL ) && ( tor->incompleteDir != NULL ) ) {
3018        char * filename = tr_buildPath( tor->incompleteDir, part, NULL );
3019        if( fileExists( filename ) ) {
3020            b = tor->incompleteDir;
3021            s = part;
3022        }
3023        tr_free( filename );
3024    }
3025
3026    if( b == NULL) {
3027        char * filename = tr_buildPath( tor->downloadDir, part, NULL );
3028        if( fileExists( filename ) ) {
3029            b = tor->downloadDir;
3030            s = part;
3031        }
3032        tr_free( filename );
3033    }
3034
3035    if( base != NULL )
3036        *base = b;
3037    if( subpath != NULL )
3038        *subpath = tr_strdup( s );
3039
3040    tr_free( part );
3041    return b != NULL;
3042}
3043
3044char*
3045tr_torrentFindFile( const tr_torrent * tor, tr_file_index_t fileNum )
3046{
3047    char * subpath;
3048    char * ret = NULL;
3049    const char * base;
3050
3051    if( tr_torrentFindFile2( tor, fileNum, &base, &subpath ) )
3052    {
3053        ret = tr_buildPath( base, subpath, NULL );
3054        tr_free( subpath );
3055    }
3056
3057    return ret;
3058}
3059
3060/* Decide whether we should be looking for files in downloadDir or incompleteDir. */
3061static void
3062refreshCurrentDir( tr_torrent * tor )
3063{
3064    const char * dir = NULL;
3065
3066    if( tor->incompleteDir == NULL )
3067        dir = tor->downloadDir;
3068    else if( !tr_torrentHasMetadata( tor ) ) /* no files to find */
3069        dir = tor->incompleteDir;
3070    else if( !tr_torrentFindFile2( tor, 0, &dir, NULL ) )
3071        dir = tor->incompleteDir;
3072
3073    assert( dir != NULL );
3074    assert( ( dir == tor->downloadDir ) || ( dir == tor->incompleteDir ) );
3075    tor->currentDir = dir;
3076}
3077
3078char*
3079tr_torrentBuildPartial( const tr_torrent * tor, tr_file_index_t fileNum )
3080{
3081    return tr_strdup_printf( "%s.part", tor->info.files[fileNum].name );
3082}
Note: See TracBrowser for help on using the repository browser.