source: trunk/libtransmission/torrent.c @ 12121

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

(trunk libT) Add an enumeration for the peer id length. Use that enum for the peer_id fields in tr_session and tr_torrent.

This also avoids an extra malloc/free per-torrent and per-session, but mostly this tweak is for the extra readability of the PEER_ID_LEN=20 enum.

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