source: trunk/libtransmission/torrent.c @ 12531

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

(trunk libT) #4336 "availablility nonsense" -- fix bug in tr_cpMissingBytesInPiece() introduced last week by r12515 for #4332. Add assertions to the nightly build to watch for regressions of this fix.

The bug was that I fixed #4332's off-by-one improperly in tr_cpMissingBlocksInPiece(). The piece's last block has to be calculated separately because its byte size may be different than the other blocks, The mistake in r12515 was that the last block could wind up being counted twice.

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