source: trunk/libtransmission/torrent.c @ 13203

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

(trunk libT) fix minor memory leak of the 'dir' string when deleting local data. (Thanks, valgrind!)

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