source: trunk/libtransmission/torrent.c @ 11746

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

(trunk libT) #3932 "Assertion failed: (tab != NULL), function tr_torrentAvailability"

Looks like tab can be NULL from the Mac client when magnet links are involved and the Mac client doesn't know how many pieces are available, so that assertion's not appropriate.

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