source: trunk/libtransmission/torrent.c @ 12667

Last change on this file since 12667 was 12667, checked in by jordan, 10 years ago

(trunk libT) #4377 "Incomplete Folder removed when sub-folder of Default Location" -- another revision to r12665

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