source: trunk/libtransmission/torrent.c @ 11706

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

cleanup of who calls the libtransmission thread locking functions.

In some cases we were calling them from deep inside libtransmission, when they should have been called directly from the public-visible API functions: tr_torrentWebSpeeds_KBps(), tr_torrentPeers(), tr_torrentTrackers().

  • Property svn:keywords set to Date Rev Author Id
File size: 81.0 KB
Line 
1/*
2 * This file Copyright (C) 2009-2010 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 11706 2011-01-18 23:44:36Z 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    assert( tr_isTorrent( tor ) );
1459    assert( tab != NULL );
1460    assert( size > 0 );
1461
1462    tr_torrentLock( tor );
1463
1464    tr_peerMgrTorrentAvailability( tor, tab, size );
1465
1466    tr_torrentUnlock( tor );
1467}
1468
1469void
1470tr_torrentAmountFinished( const tr_torrent * tor,
1471                          float *            tab,
1472                          int                size )
1473{
1474    assert( tr_isTorrent( tor ) );
1475
1476    tr_torrentLock( tor );
1477    tr_cpGetAmountDone( &tor->completion, tab, size );
1478    tr_torrentUnlock( tor );
1479}
1480
1481static void
1482tr_torrentResetTransferStats( tr_torrent * tor )
1483{
1484    assert( tr_isTorrent( tor ) );
1485
1486    tr_torrentLock( tor );
1487
1488    tor->downloadedPrev += tor->downloadedCur;
1489    tor->downloadedCur   = 0;
1490    tor->uploadedPrev   += tor->uploadedCur;
1491    tor->uploadedCur     = 0;
1492    tor->corruptPrev    += tor->corruptCur;
1493    tor->corruptCur      = 0;
1494
1495    tr_torrentSetDirty( tor );
1496
1497    tr_torrentUnlock( tor );
1498}
1499
1500void
1501tr_torrentSetHasPiece( tr_torrent *     tor,
1502                       tr_piece_index_t pieceIndex,
1503                       tr_bool          has )
1504{
1505    assert( tr_isTorrent( tor ) );
1506    assert( pieceIndex < tor->info.pieceCount );
1507
1508    if( has )
1509        tr_cpPieceAdd( &tor->completion, pieceIndex );
1510    else
1511        tr_cpPieceRem( &tor->completion, pieceIndex );
1512}
1513
1514/***
1515****
1516***/
1517
1518static void
1519freeTorrent( tr_torrent * tor )
1520{
1521    tr_torrent * t;
1522    tr_session *  session = tor->session;
1523    tr_info *    inf = &tor->info;
1524
1525    assert( tr_isTorrent( tor ) );
1526    assert( !tor->isRunning );
1527
1528    tr_sessionLock( session );
1529
1530    tr_peerMgrRemoveTorrent( tor );
1531
1532    tr_cpDestruct( &tor->completion );
1533
1534    tr_announcerRemoveTorrent( session->announcer, tor );
1535
1536    tr_free( tor->downloadDir );
1537    tr_free( tor->incompleteDir );
1538    tr_free( tor->peer_id );
1539
1540    if( tor == session->torrentList )
1541        session->torrentList = tor->next;
1542    else for( t = session->torrentList; t != NULL; t = t->next ) {
1543        if( t->next == tor ) {
1544            t->next = tor->next;
1545            break;
1546        }
1547    }
1548
1549    assert( session->torrentCount >= 1 );
1550    session->torrentCount--;
1551
1552    tr_bandwidthFree( tor->bandwidth );
1553
1554    tr_metainfoFree( inf );
1555    tr_free( tor );
1556
1557    tr_sessionUnlock( session );
1558}
1559
1560/**
1561***  Start/Stop Callback
1562**/
1563
1564static void
1565torrentStartImpl( void * vtor )
1566{
1567    time_t now;
1568    tr_torrent * tor = vtor;
1569
1570    assert( tr_isTorrent( tor ) );
1571
1572    tr_sessionLock( tor->session );
1573
1574    tr_torrentRecheckCompleteness( tor );
1575
1576    now = tr_time( );
1577    tor->isRunning = TRUE;
1578    tor->completeness = tr_cpGetStatus( &tor->completion );
1579    tor->startDate = tor->anyDate = now;
1580    tr_torrentClearError( tor );
1581    tor->finishedSeedingByIdle = FALSE;
1582
1583    tr_torrentResetTransferStats( tor );
1584    tr_announcerTorrentStarted( tor );
1585    tor->dhtAnnounceAt = now + tr_cryptoWeakRandInt( 20 );
1586    tor->dhtAnnounce6At = now + tr_cryptoWeakRandInt( 20 );
1587    tor->lpdAnnounceAt = now;
1588    tr_peerMgrStartTorrent( tor );
1589
1590    tr_sessionUnlock( tor->session );
1591}
1592
1593uint64_t
1594tr_torrentGetCurrentSizeOnDisk( const tr_torrent * tor )
1595{
1596    tr_file_index_t i;
1597    uint64_t byte_count = 0;
1598    const tr_file_index_t n = tor->info.fileCount;
1599
1600    for( i=0; i<n; ++i )
1601    {
1602        struct stat sb;
1603        char * filename = tr_torrentFindFile( tor, i );
1604
1605        sb.st_size = 0;
1606        if( filename && !stat( filename, &sb ) )
1607            byte_count += sb.st_size;
1608
1609        tr_free( filename );
1610    }
1611
1612    return byte_count;
1613}
1614
1615static void
1616torrentStart( tr_torrent * tor )
1617{
1618    assert( tr_isTorrent( tor ) );
1619
1620    /* already running... */
1621    if( tor->isRunning )
1622        return;
1623
1624    /* don't allow the torrent to be started if the files disappeared */
1625    if( setLocalErrorIfFilesDisappeared( tor ) )
1626        return;
1627
1628    /* verifying right now... wait until that's done so
1629     * we'll know what completeness to use/announce */
1630    if( tor->verifyState != TR_VERIFY_NONE ) {
1631        tor->startAfterVerify = TRUE;
1632        return;
1633    }
1634
1635    /* otherwise, start it now... */
1636    tr_sessionLock( tor->session );
1637
1638    /* allow finished torrents to be resumed */
1639    if( tr_torrentIsSeedRatioDone( tor ) ) {
1640        tr_torinf( tor, _( "Restarted manually -- disabling its seed ratio" ) );
1641        tr_torrentSetRatioMode( tor, TR_RATIOLIMIT_UNLIMITED );
1642    }
1643
1644    /* corresponds to the peer_id sent as a tracker request parameter.
1645     * one tracker admin says: "When the same torrent is opened and
1646     * closed and opened again without quitting Transmission ...
1647     * change the peerid. It would help sometimes if a stopped event
1648     * was missed to ensure that we didn't think someone was cheating. */
1649    tr_free( tor->peer_id );
1650    tor->peer_id = tr_peerIdNew( );
1651    tor->isRunning = 1;
1652    tr_torrentSetDirty( tor );
1653    tr_runInEventThread( tor->session, torrentStartImpl, tor );
1654
1655    tr_sessionUnlock( tor->session );
1656}
1657
1658void
1659tr_torrentStart( tr_torrent * tor )
1660{
1661    if( tr_isTorrent( tor ) )
1662        torrentStart( tor );
1663}
1664
1665static void
1666torrentRecheckDoneImpl( void * vtor )
1667{
1668    tr_torrent * tor = vtor;
1669    assert( tr_isTorrent( tor ) );
1670
1671    tr_torrentRecheckCompleteness( tor );
1672
1673    if( tor->startAfterVerify ) {
1674        tor->startAfterVerify = FALSE;
1675        torrentStart( tor );
1676    }
1677}
1678
1679static void
1680torrentRecheckDoneCB( tr_torrent * tor )
1681{
1682    assert( tr_isTorrent( tor ) );
1683
1684    tr_runInEventThread( tor->session, torrentRecheckDoneImpl, tor );
1685}
1686
1687static void
1688verifyTorrent( void * vtor )
1689{
1690    tr_torrent * tor = vtor;
1691
1692    assert( tr_isTorrent( tor ) );
1693    tr_sessionLock( tor->session );
1694
1695    /* if the torrent's already being verified, stop it */
1696    tr_verifyRemove( tor );
1697
1698    /* if the torrent's running, stop it & set the restart-after-verify flag */
1699    if( tor->startAfterVerify || tor->isRunning ) {
1700        /* don't clobber isStopping */
1701        const tr_bool startAfter = tor->isStopping ? FALSE : TRUE;
1702        tr_torrentStop( tor );
1703        tor->startAfterVerify = startAfter;
1704    }
1705
1706    if( setLocalErrorIfFilesDisappeared( tor ) )
1707        tor->startAfterVerify = FALSE;
1708    else
1709        tr_verifyAdd( tor, torrentRecheckDoneCB );
1710
1711    tr_sessionUnlock( tor->session );
1712}
1713
1714void
1715tr_torrentVerify( tr_torrent * tor )
1716{
1717    if( tr_isTorrent( tor ) )
1718        tr_runInEventThread( tor->session, verifyTorrent, tor );
1719}
1720
1721void
1722tr_torrentSave( tr_torrent * tor )
1723{
1724    assert( tr_isTorrent( tor ) );
1725
1726    if( tor->isDirty )
1727    {
1728        tor->isDirty = FALSE;
1729        tr_torrentSaveResume( tor );
1730    }
1731}
1732
1733static void
1734stopTorrent( void * vtor )
1735{
1736    tr_torrent * tor = vtor;
1737    tr_torinf( tor, "Pausing" );
1738
1739    assert( tr_isTorrent( tor ) );
1740
1741    tr_torrentLock( tor );
1742
1743    tr_verifyRemove( tor );
1744    tr_peerMgrStopTorrent( tor );
1745    tr_announcerTorrentStopped( tor );
1746    tr_cacheFlushTorrent( tor->session->cache, tor );
1747
1748    tr_fdTorrentClose( tor->session, tor->uniqueId );
1749
1750    if( !tor->isDeleting )
1751        tr_torrentSave( tor );
1752
1753    tr_torrentUnlock( tor );
1754}
1755
1756void
1757tr_torrentStop( tr_torrent * tor )
1758{
1759    assert( tr_isTorrent( tor ) );
1760
1761    if( tr_isTorrent( tor ) )
1762    {
1763        tr_sessionLock( tor->session );
1764
1765        tor->isRunning = 0;
1766        tor->isStopping = 0;
1767        tr_torrentSetDirty( tor );
1768        tr_runInEventThread( tor->session, stopTorrent, tor );
1769
1770        tr_sessionUnlock( tor->session );
1771    }
1772}
1773
1774static void
1775closeTorrent( void * vtor )
1776{
1777    tr_benc * d;
1778    tr_torrent * tor = vtor;
1779
1780    assert( tr_isTorrent( tor ) );
1781
1782    d = tr_bencListAddDict( &tor->session->removedTorrents, 2 );
1783    tr_bencDictAddInt( d, "id", tor->uniqueId );
1784    tr_bencDictAddInt( d, "date", tr_time( ) );
1785
1786    tr_torinf( tor, "%s", _( "Removing torrent" ) );
1787
1788    stopTorrent( tor );
1789
1790    if( tor->isDeleting )
1791    {
1792        tr_metainfoRemoveSaved( tor->session, &tor->info );
1793        tr_torrentRemoveResume( tor );
1794    }
1795
1796    tor->isRunning = 0;
1797    freeTorrent( tor );
1798}
1799
1800void
1801tr_torrentFree( tr_torrent * tor )
1802{
1803    if( tr_isTorrent( tor ) )
1804    {
1805        tr_session * session = tor->session;
1806        assert( tr_isSession( session ) );
1807        tr_sessionLock( session );
1808
1809        tr_torrentClearCompletenessCallback( tor );
1810        tr_runInEventThread( session, closeTorrent, tor );
1811
1812        tr_sessionUnlock( session );
1813    }
1814}
1815
1816struct remove_data
1817{
1818    tr_torrent   * tor;
1819    tr_bool        deleteFlag;
1820    tr_fileFunc  * deleteFunc;
1821};
1822
1823static void tr_torrentDeleteLocalData( tr_torrent *, tr_fileFunc );
1824
1825static void
1826removeTorrent( void * vdata )
1827{
1828    struct remove_data * data = vdata;
1829
1830    if( data->deleteFlag )
1831        tr_torrentDeleteLocalData( data->tor, data->deleteFunc );
1832
1833    tr_torrentClearCompletenessCallback( data->tor );
1834    closeTorrent( data->tor );
1835    tr_free( data );
1836}
1837
1838void
1839tr_torrentRemove( tr_torrent   * tor,
1840                  tr_bool        deleteFlag, 
1841                  tr_fileFunc    deleteFunc )
1842{
1843    struct remove_data * data;
1844
1845    assert( tr_isTorrent( tor ) );
1846    tor->isDeleting = 1;
1847
1848    data = tr_new0( struct remove_data, 1 );
1849    data->tor = tor;
1850    data->deleteFlag = deleteFlag;
1851    data->deleteFunc = deleteFunc;
1852    tr_runInEventThread( tor->session, removeTorrent, data );
1853}
1854
1855/**
1856***  Completeness
1857**/
1858
1859static const char *
1860getCompletionString( int type )
1861{
1862    switch( type )
1863    {
1864        /* Translators: this is a minor point that's safe to skip over, but FYI:
1865           "Complete" and "Done" are specific, different terms in Transmission:
1866           "Complete" means we've downloaded every file in the torrent.
1867           "Done" means we're done downloading the files we wanted, but NOT all
1868           that exist */
1869        case TR_PARTIAL_SEED:
1870            return _( "Done" );
1871
1872        case TR_SEED:
1873            return _( "Complete" );
1874
1875        default:
1876            return _( "Incomplete" );
1877    }
1878}
1879
1880static void
1881fireCompletenessChange( tr_torrent       * tor,
1882                        tr_completeness    status,
1883                        tr_bool            wasRunning )
1884{
1885    assert( tr_isTorrent( tor ) );
1886    assert( ( status == TR_LEECH )
1887         || ( status == TR_SEED )
1888         || ( status == TR_PARTIAL_SEED ) );
1889
1890    if( tor->completeness_func )
1891        tor->completeness_func( tor, status, wasRunning,
1892                                tor->completeness_func_user_data );
1893}
1894
1895void
1896tr_torrentSetCompletenessCallback( tr_torrent                    * tor,
1897                                   tr_torrent_completeness_func    func,
1898                                   void                          * user_data )
1899{
1900    assert( tr_isTorrent( tor ) );
1901
1902    tor->completeness_func = func;
1903    tor->completeness_func_user_data = user_data;
1904}
1905
1906void
1907tr_torrentClearCompletenessCallback( tr_torrent * torrent )
1908{
1909    tr_torrentSetCompletenessCallback( torrent, NULL, NULL );
1910}
1911
1912void
1913tr_torrentSetRatioLimitHitCallback( tr_torrent                     * tor,
1914                                    tr_torrent_ratio_limit_hit_func  func,
1915                                    void                           * user_data )
1916{
1917    assert( tr_isTorrent( tor ) );
1918
1919    tor->ratio_limit_hit_func = func;
1920    tor->ratio_limit_hit_func_user_data = user_data;
1921}
1922
1923void
1924tr_torrentClearRatioLimitHitCallback( tr_torrent * torrent )
1925{
1926    tr_torrentSetRatioLimitHitCallback( torrent, NULL, NULL );
1927}
1928
1929void
1930tr_torrentSetIdleLimitHitCallback( tr_torrent                    * tor,
1931                                   tr_torrent_idle_limit_hit_func  func,
1932                                   void                          * user_data )
1933{
1934    assert( tr_isTorrent( tor ) );
1935
1936    tor->idle_limit_hit_func = func;
1937    tor->idle_limit_hit_func_user_data = user_data;
1938}
1939
1940void
1941tr_torrentClearIdleLimitHitCallback( tr_torrent * torrent )
1942{
1943    tr_torrentSetIdleLimitHitCallback( torrent, NULL, NULL );
1944}
1945
1946static void
1947onSigCHLD( int i UNUSED )
1948{
1949    waitpid( -1, 0, WNOHANG );
1950}
1951
1952static void
1953torrentCallScript( const tr_torrent * tor, const char * script )
1954{
1955    char timeStr[128];
1956    const time_t now = tr_time( );
1957
1958    assert( tr_isTorrent( tor ) );
1959
1960    tr_strlcpy( timeStr, ctime( &now ), sizeof( timeStr ) );
1961    *strchr( timeStr,'\n' ) = '\0';
1962
1963    if( script && *script )
1964    {
1965        int i;
1966        char * cmd[] = { tr_strdup( script ), NULL };
1967        char * env[] = {
1968            tr_strdup_printf( "TR_APP_VERSION=%s", SHORT_VERSION_STRING ),
1969            tr_strdup_printf( "TR_TIME_LOCALTIME=%s", timeStr ),
1970            tr_strdup_printf( "TR_TORRENT_DIR=%s", tor->currentDir ),
1971            tr_strdup_printf( "TR_TORRENT_ID=%d", tr_torrentId( tor ) ),
1972            tr_strdup_printf( "TR_TORRENT_HASH=%s", tor->info.hashString ),
1973            tr_strdup_printf( "TR_TORRENT_NAME=%s", tr_torrentName( tor ) ),
1974            NULL };
1975
1976        tr_torinf( tor, "Calling script \"%s\"", script ); 
1977        signal( SIGCHLD, onSigCHLD );
1978
1979        if( !fork( ) )
1980        {
1981            execve( script, cmd, env );
1982            _exit( 0 );
1983        }
1984
1985        for( i=0; cmd[i]; ++i ) tr_free( cmd[i] );
1986        for( i=0; env[i]; ++i ) tr_free( env[i] );
1987    }
1988}
1989
1990void
1991tr_torrentRecheckCompleteness( tr_torrent * tor )
1992{
1993    tr_completeness completeness;
1994
1995    assert( tr_isTorrent( tor ) );
1996
1997    tr_torrentLock( tor );
1998
1999    completeness = tr_cpGetStatus( &tor->completion );
2000
2001    if( completeness != tor->completeness )
2002    {
2003        const int recentChange = tor->downloadedCur != 0;
2004        const tr_bool wasLeeching = !tr_torrentIsSeed( tor );
2005        const tr_bool wasRunning = tor->isRunning;
2006
2007        if( recentChange )
2008        {
2009            tr_torinf( tor, _( "State changed from \"%1$s\" to \"%2$s\"" ),
2010                      getCompletionString( tor->completeness ),
2011                      getCompletionString( completeness ) );
2012        }
2013
2014        tor->completeness = completeness;
2015        tr_fdTorrentClose( tor->session, tor->uniqueId );
2016
2017        if( tr_torrentIsSeed( tor ) )
2018        {
2019            if( recentChange )
2020            {
2021                tr_announcerTorrentCompleted( tor );
2022                tor->doneDate = tor->anyDate = tr_time( );
2023            }
2024
2025            if( wasLeeching && wasRunning )
2026            {
2027                /* clear interested flag on all peers */
2028                tr_peerMgrClearInterest( tor );
2029
2030                /* if completeness was TR_LEECH then the seed limit check will have been skipped in bandwidthPulse */
2031                tr_torrentCheckSeedLimit( tor );
2032            }
2033
2034            if( tor->currentDir == tor->incompleteDir )
2035                tr_torrentSetLocation( tor, tor->downloadDir, TRUE, NULL, NULL );
2036
2037            if( tr_sessionIsTorrentDoneScriptEnabled( tor->session ) )
2038                torrentCallScript( tor, tr_sessionGetTorrentDoneScript( tor->session ) );
2039        }
2040
2041        fireCompletenessChange( tor, wasRunning, completeness );
2042
2043        tr_torrentSetDirty( tor );
2044    }
2045
2046    tr_torrentUnlock( tor );
2047}
2048
2049/***
2050****
2051***/
2052
2053static void
2054tr_torrentFireMetadataCompleted( tr_torrent * tor )
2055{
2056    assert( tr_isTorrent( tor ) );
2057
2058    if( tor->metadata_func )
2059        tor->metadata_func( tor, tor->metadata_func_user_data );
2060}
2061
2062void
2063tr_torrentSetMetadataCallback( tr_torrent                * tor,
2064                               tr_torrent_metadata_func    func,
2065                               void                      * user_data )
2066{
2067    assert( tr_isTorrent( tor ) );
2068
2069    tor->metadata_func = func;
2070    tor->metadata_func_user_data = user_data;
2071}
2072
2073
2074/**
2075***  File priorities
2076**/
2077
2078void
2079tr_torrentInitFilePriority( tr_torrent *    tor,
2080                            tr_file_index_t fileIndex,
2081                            tr_priority_t   priority )
2082{
2083    tr_piece_index_t i;
2084    tr_file *        file;
2085
2086    assert( tr_isTorrent( tor ) );
2087    assert( fileIndex < tor->info.fileCount );
2088    assert( tr_isPriority( priority ) );
2089
2090    file = &tor->info.files[fileIndex];
2091    file->priority = priority;
2092    for( i = file->firstPiece; i <= file->lastPiece; ++i )
2093        tor->info.pieces[i].priority = calculatePiecePriority( tor, i, fileIndex );
2094}
2095
2096void
2097tr_torrentSetFilePriorities( tr_torrent             * tor,
2098                             const tr_file_index_t  * files,
2099                             tr_file_index_t          fileCount,
2100                             tr_priority_t            priority )
2101{
2102    tr_file_index_t i;
2103    assert( tr_isTorrent( tor ) );
2104    tr_torrentLock( tor );
2105
2106    for( i = 0; i < fileCount; ++i )
2107        if( files[i] < tor->info.fileCount )
2108            tr_torrentInitFilePriority( tor, files[i], priority );
2109    tr_torrentSetDirty( tor );
2110    tr_peerMgrRebuildRequests( tor );
2111
2112    tr_torrentUnlock( tor );
2113}
2114
2115tr_priority_t*
2116tr_torrentGetFilePriorities( const tr_torrent * tor )
2117{
2118    tr_file_index_t i;
2119    tr_priority_t * p;
2120
2121    assert( tr_isTorrent( tor ) );
2122
2123    tr_torrentLock( tor );
2124    p = tr_new0( tr_priority_t, tor->info.fileCount );
2125    for( i = 0; i < tor->info.fileCount; ++i )
2126        p[i] = tor->info.files[i].priority;
2127    tr_torrentUnlock( tor );
2128
2129    return p;
2130}
2131
2132/**
2133***  File DND
2134**/
2135
2136static void
2137setFileDND( tr_torrent * tor, tr_file_index_t fileIndex, int doDownload )
2138{
2139    tr_file *        file;
2140    const int8_t     dnd = !doDownload;
2141    tr_piece_index_t firstPiece;
2142    int8_t           firstPieceDND;
2143    tr_piece_index_t lastPiece;
2144    int8_t           lastPieceDND;
2145    tr_file_index_t  i;
2146
2147    assert( tr_isTorrent( tor ) );
2148
2149    file = &tor->info.files[fileIndex];
2150    file->dnd = dnd;
2151    firstPiece = file->firstPiece;
2152    lastPiece = file->lastPiece;
2153
2154    /* can't set the first piece to DND unless
2155       every file using that piece is DND */
2156    firstPieceDND = dnd;
2157    if( fileIndex > 0 )
2158    {
2159        for( i = fileIndex - 1; firstPieceDND; --i )
2160        {
2161            if( tor->info.files[i].lastPiece != firstPiece )
2162                break;
2163            firstPieceDND = tor->info.files[i].dnd;
2164            if( !i )
2165                break;
2166        }
2167    }
2168
2169    /* can't set the last piece to DND unless
2170       every file using that piece is DND */
2171    lastPieceDND = dnd;
2172    for( i = fileIndex + 1; lastPieceDND && i < tor->info.fileCount; ++i )
2173    {
2174        if( tor->info.files[i].firstPiece != lastPiece )
2175            break;
2176        lastPieceDND = tor->info.files[i].dnd;
2177    }
2178
2179    if( firstPiece == lastPiece )
2180    {
2181        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
2182    }
2183    else
2184    {
2185        tr_piece_index_t pp;
2186        tor->info.pieces[firstPiece].dnd = firstPieceDND;
2187        tor->info.pieces[lastPiece].dnd = lastPieceDND;
2188        for( pp = firstPiece + 1; pp < lastPiece; ++pp )
2189            tor->info.pieces[pp].dnd = dnd;
2190    }
2191}
2192
2193void
2194tr_torrentInitFileDLs( tr_torrent             * tor,
2195                       const tr_file_index_t  * files,
2196                       tr_file_index_t          fileCount,
2197                       tr_bool                  doDownload )
2198{
2199    tr_file_index_t i;
2200
2201    assert( tr_isTorrent( tor ) );
2202
2203    tr_torrentLock( tor );
2204
2205    for( i=0; i<fileCount; ++i )
2206        if( files[i] < tor->info.fileCount )
2207            setFileDND( tor, files[i], doDownload );
2208
2209    tr_cpInvalidateDND( &tor->completion );
2210
2211    tr_torrentUnlock( tor );
2212}
2213
2214void
2215tr_torrentSetFileDLs( tr_torrent             * tor,
2216                      const tr_file_index_t  * files,
2217                      tr_file_index_t          fileCount,
2218                      tr_bool                  doDownload )
2219{
2220    assert( tr_isTorrent( tor ) );
2221    tr_torrentLock( tor );
2222
2223    tr_torrentInitFileDLs( tor, files, fileCount, doDownload );
2224    tr_torrentSetDirty( tor );
2225    tr_peerMgrRebuildRequests( tor );
2226
2227    tr_torrentUnlock( tor );
2228}
2229
2230/***
2231****
2232***/
2233
2234tr_priority_t
2235tr_torrentGetPriority( const tr_torrent * tor )
2236{
2237    assert( tr_isTorrent( tor ) );
2238
2239    return tor->bandwidth->priority;
2240}
2241
2242void
2243tr_torrentSetPriority( tr_torrent * tor, tr_priority_t priority )
2244{
2245    assert( tr_isTorrent( tor ) );
2246    assert( tr_isPriority( priority ) );
2247
2248    if( tor->bandwidth->priority != priority )
2249    {
2250        tor->bandwidth->priority = priority;
2251
2252        tr_torrentSetDirty( tor );
2253    }
2254}
2255
2256/***
2257****
2258***/
2259
2260void
2261tr_torrentSetPeerLimit( tr_torrent * tor,
2262                        uint16_t     maxConnectedPeers )
2263{
2264    assert( tr_isTorrent( tor ) );
2265
2266    if ( tor->maxConnectedPeers != maxConnectedPeers )
2267    {
2268        tor->maxConnectedPeers = maxConnectedPeers;
2269
2270        tr_torrentSetDirty( tor );
2271    }
2272}
2273
2274uint16_t
2275tr_torrentGetPeerLimit( const tr_torrent * tor )
2276{
2277    assert( tr_isTorrent( tor ) );
2278
2279    return tor->maxConnectedPeers;
2280}
2281
2282/***
2283****
2284***/
2285
2286tr_block_index_t
2287_tr_block( const tr_torrent * tor,
2288           tr_piece_index_t   index,
2289           uint32_t           offset )
2290{
2291    tr_block_index_t ret;
2292
2293    assert( tr_isTorrent( tor ) );
2294
2295    ret = index;
2296    ret *= ( tor->info.pieceSize / tor->blockSize );
2297    ret += offset / tor->blockSize;
2298    return ret;
2299}
2300
2301tr_bool
2302tr_torrentReqIsValid( const tr_torrent * tor,
2303                      tr_piece_index_t   index,
2304                      uint32_t           offset,
2305                      uint32_t           length )
2306{
2307    int err = 0;
2308
2309    assert( tr_isTorrent( tor ) );
2310
2311    if( index >= tor->info.pieceCount )
2312        err = 1;
2313    else if( length < 1 )
2314        err = 2;
2315    else if( ( offset + length ) > tr_torPieceCountBytes( tor, index ) )
2316        err = 3;
2317    else if( length > MAX_BLOCK_SIZE )
2318        err = 4;
2319    else if( tr_pieceOffset( tor, index, offset, length ) > tor->info.totalSize )
2320        err = 5;
2321
2322    if( err ) tr_tordbg( tor, "index %lu offset %lu length %lu err %d\n",
2323                              (unsigned long)index,
2324                              (unsigned long)offset,
2325                              (unsigned long)length,
2326                              err );
2327
2328    return !err;
2329}
2330
2331uint64_t
2332tr_pieceOffset( const tr_torrent * tor,
2333                tr_piece_index_t   index,
2334                uint32_t           offset,
2335                uint32_t           length )
2336{
2337    uint64_t ret;
2338
2339    assert( tr_isTorrent( tor ) );
2340
2341    ret = tor->info.pieceSize;
2342    ret *= index;
2343    ret += offset;
2344    ret += length;
2345    return ret;
2346}
2347
2348/***
2349****
2350***/
2351
2352void
2353tr_torrentSetPieceChecked( tr_torrent * tor, tr_piece_index_t pieceIndex )
2354{
2355    assert( tr_isTorrent( tor ) );
2356    assert( pieceIndex < tor->info.pieceCount );
2357
2358    tor->info.pieces[pieceIndex].timeChecked = tr_time( );
2359}
2360
2361void
2362tr_torrentSetChecked( tr_torrent * tor, time_t when )
2363{
2364    tr_piece_index_t i, n;
2365
2366    assert( tr_isTorrent( tor ) );
2367
2368    for( i=0, n=tor->info.pieceCount; i!=n; ++i )
2369        tor->info.pieces[i].timeChecked = when;
2370}
2371
2372tr_bool
2373tr_torrentCheckPiece( tr_torrent * tor, tr_piece_index_t pieceIndex )
2374{
2375    const tr_bool pass = tr_ioTestPiece( tor, pieceIndex );
2376
2377    tr_deeplog_tor( tor, "[LAZY] tr_torrentCheckPiece tested piece %zu, pass==%d", (size_t)pieceIndex, (int)pass );
2378    tr_torrentSetHasPiece( tor, pieceIndex, pass );
2379    tr_torrentSetPieceChecked( tor, pieceIndex );
2380    tor->anyDate = tr_time( );
2381    tr_torrentSetDirty( tor );
2382
2383    return pass;
2384}
2385
2386static time_t
2387getFileMTime( const tr_torrent * tor, tr_file_index_t i )
2388{
2389    struct stat sb;
2390    time_t mtime = 0;
2391    char * path = tr_torrentFindFile( tor, i );
2392
2393    if( ( path != NULL ) && !stat( path, &sb ) && S_ISREG( sb.st_mode ) )
2394    {
2395#ifdef SYS_DARWIN
2396        mtime = sb.st_mtimespec.tv_sec;
2397#else
2398        mtime = sb.st_mtime;
2399#endif
2400    }
2401
2402    tr_free( path );
2403    return mtime;
2404}
2405
2406tr_bool
2407tr_torrentPieceNeedsCheck( const tr_torrent * tor, tr_piece_index_t p )
2408{
2409    uint64_t unused;
2410    tr_file_index_t f;
2411    const tr_info * inf = tr_torrentInfo( tor );
2412
2413    /* if we've never checked this piece, then it needs to be checked */
2414    if( !inf->pieces[p].timeChecked )
2415        return TRUE;
2416
2417    /* If we think we've completed one of the files in this piece,
2418     * but it's been modified since we last checked it,
2419     * then it needs to be rechecked */
2420    tr_ioFindFileLocation( tor, p, 0, &f, &unused );
2421    for( ; f < inf->fileCount && pieceHasFile( p, &inf->files[f] ); ++f )
2422        if( tr_cpFileIsComplete( &tor->completion, f ) )
2423            if( getFileMTime( tor, f ) > inf->pieces[p].timeChecked )
2424                return TRUE;
2425
2426    return FALSE;
2427}
2428
2429/***
2430****
2431***/
2432
2433static int
2434compareTrackerByTier( const void * va, const void * vb )
2435{
2436    const tr_tracker_info * a = va;
2437    const tr_tracker_info * b = vb;
2438
2439    /* sort by tier */
2440    if( a->tier != b->tier )
2441        return a->tier - b->tier;
2442
2443    /* get the effects of a stable sort by comparing the two elements' addresses */
2444    return a - b;
2445}
2446
2447tr_bool
2448tr_torrentSetAnnounceList( tr_torrent             * tor,
2449                           const tr_tracker_info  * trackers_in,
2450                           int                      trackerCount )
2451{
2452    int i;
2453    tr_benc metainfo;
2454    tr_bool ok = TRUE;
2455    tr_tracker_info * trackers;
2456
2457    tr_torrentLock( tor );
2458
2459    assert( tr_isTorrent( tor ) );
2460
2461    /* ensure the trackers' tiers are in ascending order */
2462    trackers = tr_memdup( trackers_in, sizeof( tr_tracker_info ) * trackerCount );
2463    qsort( trackers, trackerCount, sizeof( tr_tracker_info ), compareTrackerByTier );
2464
2465    /* look for bad URLs */
2466    for( i=0; ok && i<trackerCount; ++i )
2467        if( !tr_urlIsValidTracker( trackers[i].announce ) )
2468            ok = FALSE;
2469
2470    /* save to the .torrent file */
2471    if( ok && !tr_bencLoadFile( &metainfo, TR_FMT_BENC, tor->info.torrent ) )
2472    {
2473        tr_bool hasInfo;
2474        tr_info tmpInfo;
2475
2476        /* remove the old fields */
2477        tr_bencDictRemove( &metainfo, "announce" );
2478        tr_bencDictRemove( &metainfo, "announce-list" );
2479
2480        /* add the new fields */
2481        if( trackerCount > 0 )
2482        {
2483            tr_bencDictAddStr( &metainfo, "announce", trackers[0].announce );
2484        }
2485        if( trackerCount > 1 )
2486        {
2487            int i;
2488            int prevTier = -1;
2489            tr_benc * tier = NULL;
2490            tr_benc * announceList = tr_bencDictAddList( &metainfo, "announce-list", 0 );
2491
2492            for( i=0; i<trackerCount; ++i ) {
2493                if( prevTier != trackers[i].tier ) {
2494                    prevTier = trackers[i].tier;
2495                    tier = tr_bencListAddList( announceList, 0 );
2496                }
2497                tr_bencListAddStr( tier, trackers[i].announce );
2498            }
2499        }
2500
2501        /* try to parse it back again, to make sure it's good */
2502        memset( &tmpInfo, 0, sizeof( tr_info ) );
2503        if( tr_metainfoParse( tor->session, &metainfo, &tmpInfo,
2504                              &hasInfo, &tor->infoDictLength ) )
2505        {
2506            /* it's good, so keep these new trackers and free the old ones */
2507
2508            tr_info swap;
2509            swap.trackers = tor->info.trackers;
2510            swap.trackerCount = tor->info.trackerCount;
2511            tor->info.trackers = tmpInfo.trackers;
2512            tor->info.trackerCount = tmpInfo.trackerCount;
2513            tmpInfo.trackers = swap.trackers;
2514            tmpInfo.trackerCount = swap.trackerCount;
2515
2516            tr_metainfoFree( &tmpInfo );
2517            tr_bencToFile( &metainfo, TR_FMT_BENC, tor->info.torrent );
2518        }
2519
2520        /* cleanup */
2521        tr_bencFree( &metainfo );
2522
2523        /* if we had a tracker-related error on this torrent,
2524         * and that tracker's been removed,
2525         * then clear the error */
2526        if(    ( tor->error == TR_STAT_TRACKER_WARNING )
2527            || ( tor->error == TR_STAT_TRACKER_ERROR ) )
2528        {
2529            tr_bool clear = TRUE;
2530
2531            for( i=0; clear && i<trackerCount; ++i )
2532                if( !strcmp( trackers[i].announce, tor->errorTracker ) )
2533                    clear = FALSE;
2534
2535            if( clear )
2536                tr_torrentClearError( tor );
2537        }
2538
2539        /* tell the announcer to reload this torrent's tracker list */
2540        tr_announcerResetTorrent( tor->session->announcer, tor );
2541    }
2542
2543    tr_torrentUnlock( tor );
2544
2545    tr_free( trackers );
2546    return ok;
2547}
2548
2549/**
2550***
2551**/
2552
2553void
2554tr_torrentSetAddedDate( tr_torrent * tor,
2555                        time_t       t )
2556{
2557    assert( tr_isTorrent( tor ) );
2558
2559    tor->addedDate = t;
2560    tor->anyDate = MAX( tor->anyDate, tor->addedDate );
2561}
2562
2563void
2564tr_torrentSetActivityDate( tr_torrent * tor, time_t t )
2565{
2566    assert( tr_isTorrent( tor ) );
2567
2568    tor->activityDate = t;
2569    tor->anyDate = MAX( tor->anyDate, tor->activityDate );
2570}
2571
2572void
2573tr_torrentSetDoneDate( tr_torrent * tor,
2574                       time_t       t )
2575{
2576    assert( tr_isTorrent( tor ) );
2577
2578    tor->doneDate = t;
2579    tor->anyDate = MAX( tor->anyDate, tor->doneDate );
2580}
2581
2582/**
2583***
2584**/
2585
2586uint64_t
2587tr_torrentGetBytesLeftToAllocate( const tr_torrent * tor )
2588{
2589    tr_file_index_t i;
2590    uint64_t bytesLeft = 0;
2591
2592    assert( tr_isTorrent( tor ) );
2593
2594    for( i=0; i<tor->info.fileCount; ++i )
2595    {
2596        if( !tor->info.files[i].dnd )
2597        {
2598            struct stat sb;
2599            const uint64_t length = tor->info.files[i].length;
2600            char * path = tr_torrentFindFile( tor, i );
2601
2602            bytesLeft += length;
2603
2604            if( ( path != NULL ) && !stat( path, &sb )
2605                                 && S_ISREG( sb.st_mode )
2606                                 && ( (uint64_t)sb.st_size <= length ) )
2607                bytesLeft -= sb.st_size;
2608
2609            tr_free( path );
2610        }
2611    }
2612
2613    return bytesLeft;
2614}
2615
2616/****
2617*****  Removing the torrent's local data
2618****/
2619
2620static int
2621vstrcmp( const void * a, const void * b )
2622{
2623    return strcmp( a, b );
2624}
2625
2626static int
2627compareLongestFirst( const void * a, const void * b )
2628{
2629    const size_t alen = strlen( a );
2630    const size_t blen = strlen( b );
2631
2632    if( alen != blen )
2633        return alen > blen ? -1 : 1;
2634
2635    return vstrcmp( a, b );
2636}
2637
2638static void
2639addDirtyFile( const char  * root,
2640              const char  * filename,
2641              tr_ptrArray * dirtyFolders )
2642{
2643    char * dir = tr_dirname( filename );
2644
2645    /* add the parent folders to dirtyFolders until we reach the root or a known-dirty */
2646    while (     ( dir != NULL )
2647             && ( strlen( root ) <= strlen( dir ) )
2648             && ( tr_ptrArrayFindSorted( dirtyFolders, dir, vstrcmp ) == NULL ) )
2649    {
2650        char * tmp;
2651        tr_ptrArrayInsertSorted( dirtyFolders, tr_strdup( dir ), vstrcmp );
2652
2653        tmp = tr_dirname( dir );
2654        tr_free( dir );
2655        dir = tmp;
2656    }
2657
2658    tr_free( dir );
2659}
2660
2661static void
2662walkLocalData( const tr_torrent * tor,
2663               const char       * root,
2664               const char       * dir,
2665               const char       * base,
2666               tr_ptrArray      * torrentFiles,
2667               tr_ptrArray      * folders,
2668               tr_ptrArray      * dirtyFolders )
2669{
2670    int i;
2671    struct stat sb;
2672    char * buf;
2673
2674    assert( tr_isTorrent( tor ) );
2675
2676    buf = tr_buildPath( dir, base, NULL );
2677    i = stat( buf, &sb );
2678    if( !i )
2679    {
2680        DIR * odir = NULL;
2681
2682        if( S_ISDIR( sb.st_mode ) && ( ( odir = opendir ( buf ) ) ) )
2683        {
2684            struct dirent *d;
2685            tr_ptrArrayInsertSorted( folders, tr_strdup( buf ), vstrcmp );
2686            for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
2687                if( d->d_name && strcmp( d->d_name, "." ) && strcmp( d->d_name, ".." ) )
2688                    walkLocalData( tor, root, buf, d->d_name, torrentFiles, folders, dirtyFolders );
2689            closedir( odir );
2690        }
2691        else if( S_ISREG( sb.st_mode ) && ( sb.st_size > 0 ) )
2692        {
2693            const char * sub = buf + strlen( tor->currentDir ) + strlen( TR_PATH_DELIMITER_STR );
2694            const tr_bool isTorrentFile = tr_ptrArrayFindSorted( torrentFiles, sub, vstrcmp ) != NULL;
2695            if( !isTorrentFile )
2696                addDirtyFile( root, buf, dirtyFolders );
2697        }
2698    }
2699
2700    tr_free( buf );
2701}
2702
2703static void
2704deleteLocalFile( const char * filename, tr_fileFunc fileFunc )
2705{
2706    struct stat sb;
2707    if( !stat( filename, &sb ) ) /* if file exists... */
2708        fileFunc( filename );
2709}
2710
2711static void
2712deleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
2713{
2714    int i, n;
2715    char ** s;
2716    tr_file_index_t f;
2717    tr_ptrArray torrentFiles = TR_PTR_ARRAY_INIT;
2718    tr_ptrArray folders      = TR_PTR_ARRAY_INIT;
2719    tr_ptrArray dirtyFolders = TR_PTR_ARRAY_INIT; /* dirty == contains non-torrent files */
2720
2721    const char * firstFile = tor->info.files[0].name;
2722    const char * cpch = strchr( firstFile, TR_PATH_DELIMITER );
2723    char * tmp = cpch ? tr_strndup( firstFile, cpch - firstFile ) : NULL;
2724    char * root = tr_buildPath( tor->currentDir, tmp, NULL );
2725
2726    assert( tr_isTorrent( tor ) );
2727
2728    for( f=0; f<tor->info.fileCount; ++f ) {
2729        tr_ptrArrayInsertSorted( &torrentFiles, tr_strdup( tor->info.files[f].name ), vstrcmp );
2730        tr_ptrArrayInsertSorted( &torrentFiles, tr_torrentBuildPartial( tor, f ), vstrcmp );
2731    }
2732
2733    /* build the set of folders and dirtyFolders */
2734    walkLocalData( tor, root, root, NULL, &torrentFiles, &folders, &dirtyFolders );
2735
2736    /* try to remove entire folders first, so that the recycle bin will be tidy */
2737    s = (char**) tr_ptrArrayPeek( &folders, &n );
2738    for( i=0; i<n; ++i )
2739        if( tr_ptrArrayFindSorted( &dirtyFolders, s[i], vstrcmp ) == NULL )
2740            deleteLocalFile( s[i], fileFunc );
2741
2742    /* now blow away any remaining torrent files, such as torrent files in dirty folders */
2743    for( i=0, n=tr_ptrArraySize( &torrentFiles ); i<n; ++i ) {
2744        char * path = tr_buildPath( tor->currentDir, tr_ptrArrayNth( &torrentFiles, i ), NULL );
2745        deleteLocalFile( path, fileFunc );
2746        tr_free( path );
2747    }
2748
2749    /* Now clean out the directories left empty from the previous step.
2750     * Work from deepest to shallowest s.t. lower folders
2751     * won't prevent the upper folders from being deleted */
2752    {
2753        tr_ptrArray cleanFolders = TR_PTR_ARRAY_INIT;
2754        s = (char**) tr_ptrArrayPeek( &folders, &n );
2755        for( i=0; i<n; ++i )
2756            if( tr_ptrArrayFindSorted( &dirtyFolders, s[i], vstrcmp ) == NULL )
2757                tr_ptrArrayInsertSorted( &cleanFolders, s[i], compareLongestFirst );
2758        s = (char**) tr_ptrArrayPeek( &cleanFolders, &n );
2759        for( i=0; i<n; ++i ) {
2760#ifdef SYS_DARWIN
2761            char * dsStore = tr_buildPath( s[i], ".DS_Store", NULL );
2762            deleteLocalFile( dsStore, fileFunc );
2763            tr_free( dsStore );
2764#endif
2765            deleteLocalFile( s[i], fileFunc );
2766        }
2767        tr_ptrArrayDestruct( &cleanFolders, NULL );
2768    }
2769
2770    /* cleanup */
2771    tr_ptrArrayDestruct( &dirtyFolders, tr_free );
2772    tr_ptrArrayDestruct( &folders, tr_free );
2773    tr_ptrArrayDestruct( &torrentFiles, tr_free );
2774    tr_free( root );
2775    tr_free( tmp );
2776}
2777
2778static void
2779tr_torrentDeleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
2780{
2781    assert( tr_isTorrent( tor ) );
2782
2783    if( fileFunc == NULL )
2784        fileFunc = remove;
2785
2786    /* close all the files because we're about to delete them */
2787    tr_cacheFlushTorrent( tor->session->cache, tor );
2788    tr_fdTorrentClose( tor->session, tor->uniqueId );
2789
2790    if( tor->info.fileCount > 1 )
2791    {
2792        deleteLocalData( tor, fileFunc );
2793    }
2794    else if( tor->info.fileCount == 1 )
2795    {
2796        char * tmp;
2797
2798        /* torrent only has one file */
2799        char * path = tr_buildPath( tor->currentDir, tor->info.files[0].name, NULL );
2800        deleteLocalFile( path, fileFunc );
2801        tr_free( path );
2802
2803        tmp = tr_torrentBuildPartial( tor, 0 );
2804        path = tr_buildPath( tor->currentDir, tmp, NULL );
2805        deleteLocalFile( path, fileFunc );
2806        tr_free( path );
2807        tr_free( tmp );
2808    }
2809}
2810
2811/***
2812****
2813***/
2814
2815struct LocationData
2816{
2817    tr_bool move_from_old_location;
2818    int * setme_state;
2819    double * setme_progress;
2820    char * location;
2821    tr_torrent * tor;
2822};
2823
2824static void
2825setLocation( void * vdata )
2826{
2827    tr_bool err = FALSE;
2828    struct LocationData * data = vdata;
2829    tr_torrent * tor = data->tor;
2830    const tr_bool do_move = data->move_from_old_location;
2831    const char * location = data->location;
2832    double bytesHandled = 0;
2833
2834    assert( tr_isTorrent( tor ) );
2835
2836    tr_dbg( "Moving \"%s\" location from currentDir \"%s\" to \"%s\"",
2837            tr_torrentName(tor), tor->currentDir, location );
2838
2839    tr_mkdirp( location, 0777 );
2840
2841    if( !tr_is_same_file( location, tor->currentDir ) )
2842    {
2843        tr_file_index_t i;
2844
2845        /* bad idea to move files while they're being verified... */
2846        tr_verifyRemove( tor );
2847
2848        /* try to move the files.
2849         * FIXME: there are still all kinds of nasty cases, like what
2850         * if the target directory runs out of space halfway through... */
2851        for( i=0; !err && i<tor->info.fileCount; ++i )
2852        {
2853            const tr_file * f = &tor->info.files[i];
2854            const char * oldbase;
2855            char * sub;
2856            if( tr_torrentFindFile2( tor, i, &oldbase, &sub ) )
2857            {
2858                char * oldpath = tr_buildPath( oldbase, sub, NULL );
2859                char * newpath = tr_buildPath( location, sub, NULL );
2860
2861                tr_dbg( "Found file #%d: %s", (int)i, oldpath );
2862
2863                if( do_move )
2864                {
2865                    tr_bool renamed = FALSE;
2866                    errno = 0;
2867                    tr_torinf( tor, "moving \"%s\" to \"%s\"", oldpath, newpath );
2868                    if( tr_moveFile( oldpath, newpath, &renamed ) )
2869                    {
2870                        err = TRUE;
2871                        tr_torerr( tor, "error moving \"%s\" to \"%s\": %s",
2872                                        oldpath, newpath, tr_strerror( errno ) );
2873                    }
2874                }
2875
2876                tr_free( newpath );
2877                tr_free( oldpath );
2878                tr_free( sub );
2879            }
2880
2881            if( data->setme_progress )
2882            {
2883                bytesHandled += f->length;
2884                *data->setme_progress = bytesHandled / tor->info.totalSize;
2885            }
2886        }
2887
2888        if( !err )
2889        {
2890            /* blow away the leftover subdirectories in the old location */
2891            if( do_move )
2892                tr_torrentDeleteLocalData( tor, remove );
2893
2894            /* set the new location and reverify */
2895            tr_torrentSetDownloadDir( tor, location );
2896        }
2897    }
2898
2899    if( !err && do_move )
2900    {
2901        tr_free( tor->incompleteDir );
2902        tor->incompleteDir = NULL;
2903        tor->currentDir = tor->downloadDir;
2904    }
2905
2906    if( data->setme_state )
2907        *data->setme_state = err ? TR_LOC_ERROR : TR_LOC_DONE;
2908
2909    /* cleanup */
2910    tr_free( data->location );
2911    tr_free( data );
2912}
2913
2914void
2915tr_torrentSetLocation( tr_torrent  * tor,
2916                       const char  * location,
2917                       tr_bool       move_from_old_location,
2918                       double      * setme_progress,
2919                       int         * setme_state )
2920{
2921    struct LocationData * data;
2922
2923    assert( tr_isTorrent( tor ) );
2924
2925    if( setme_state )
2926        *setme_state = TR_LOC_MOVING;
2927    if( setme_progress )
2928        *setme_progress = 0;
2929
2930    /* run this in the libtransmission thread */
2931    data = tr_new( struct LocationData, 1 );
2932    data->tor = tor;
2933    data->location = tr_strdup( location );
2934    data->move_from_old_location = move_from_old_location;
2935    data->setme_state = setme_state;
2936    data->setme_progress = setme_progress;
2937    tr_runInEventThread( tor->session, setLocation, data );
2938}
2939
2940/***
2941****
2942***/
2943
2944void
2945tr_torrentFileCompleted( tr_torrent * tor, tr_file_index_t fileNum )
2946{
2947    char * sub;
2948    const char * base;
2949
2950    /* close the file so that we can reopen in read-only mode as needed */
2951    tr_fdFileClose( tor->session, tor, fileNum );
2952
2953    /* if the torrent's current filename isn't the same as the one in the
2954     * metadata -- for example, if it had the ".part" suffix appended to
2955     * it until now -- then rename it to match the one in the metadata */
2956    if( tr_torrentFindFile2( tor, fileNum, &base, &sub ) )
2957    {
2958        const tr_file * file = &tor->info.files[fileNum];
2959
2960        if( strcmp( sub, file->name ) )
2961        {
2962            char * oldpath = tr_buildPath( base, sub, NULL );
2963            char * newpath = tr_buildPath( base, file->name, NULL );
2964
2965            if( rename( oldpath, newpath ) )
2966                tr_torerr( tor, "Error moving \"%s\" to \"%s\": %s", oldpath, newpath, tr_strerror( errno ) );
2967
2968            tr_free( newpath );
2969            tr_free( oldpath );
2970        }
2971
2972        tr_free( sub );
2973    }
2974}
2975
2976/***
2977****
2978***/
2979
2980static tr_bool
2981fileExists( const char * filename )
2982{
2983    struct stat sb;
2984    const tr_bool ok = !stat( filename, &sb );
2985    return ok;
2986}
2987
2988tr_bool
2989tr_torrentFindFile2( const tr_torrent * tor, tr_file_index_t fileNum,
2990                     const char ** base, char ** subpath )
2991{
2992    char * part;
2993    const tr_file * file;
2994    const char * b = NULL;
2995    const char * s = NULL;
2996
2997    assert( tr_isTorrent( tor ) );
2998    assert( fileNum < tor->info.fileCount );
2999
3000    file = &tor->info.files[fileNum];
3001    part = tr_torrentBuildPartial( tor, fileNum );
3002
3003    if( b == NULL ) {
3004        char * filename = tr_buildPath( tor->downloadDir, file->name, NULL );
3005        if( fileExists( filename ) ) {
3006            b = tor->downloadDir;
3007            s = file->name;
3008        }
3009        tr_free( filename );
3010    }
3011
3012    if( ( b == NULL ) && ( tor->incompleteDir != NULL ) ) {
3013        char * filename = tr_buildPath( tor->incompleteDir, file->name, NULL );
3014        if( fileExists( filename ) ) {
3015            b = tor->incompleteDir;
3016            s = file->name;
3017        }
3018        tr_free( filename );
3019    }
3020
3021    if( ( b == NULL ) && ( tor->incompleteDir != NULL ) ) {
3022        char * filename = tr_buildPath( tor->incompleteDir, part, NULL );
3023        if( fileExists( filename ) ) {
3024            b = tor->incompleteDir;
3025            s = part;
3026        }
3027        tr_free( filename );
3028    }
3029
3030    if( b == NULL) {
3031        char * filename = tr_buildPath( tor->downloadDir, part, NULL );
3032        if( fileExists( filename ) ) {
3033            b = tor->downloadDir;
3034            s = part;
3035        }
3036        tr_free( filename );
3037    }
3038
3039    if( base != NULL )
3040        *base = b;
3041    if( subpath != NULL )
3042        *subpath = tr_strdup( s );
3043
3044    tr_free( part );
3045    return b != NULL;
3046}
3047
3048char*
3049tr_torrentFindFile( const tr_torrent * tor, tr_file_index_t fileNum )
3050{
3051    char * subpath;
3052    char * ret = NULL;
3053    const char * base;
3054
3055    if( tr_torrentFindFile2( tor, fileNum, &base, &subpath ) )
3056    {
3057        ret = tr_buildPath( base, subpath, NULL );
3058        tr_free( subpath );
3059    }
3060
3061    return ret;
3062}
3063
3064/* Decide whether we should be looking for files in downloadDir or incompleteDir. */
3065static void
3066refreshCurrentDir( tr_torrent * tor )
3067{
3068    const char * dir = NULL;
3069
3070    if( tor->incompleteDir == NULL )
3071        dir = tor->downloadDir;
3072    else if( !tr_torrentHasMetadata( tor ) ) /* no files to find */
3073        dir = tor->incompleteDir;
3074    else if( !tr_torrentFindFile2( tor, 0, &dir, NULL ) )
3075        dir = tor->incompleteDir;
3076
3077    assert( dir != NULL );
3078    assert( ( dir == tor->downloadDir ) || ( dir == tor->incompleteDir ) );
3079    tor->currentDir = dir;
3080}
3081
3082char*
3083tr_torrentBuildPartial( const tr_torrent * tor, tr_file_index_t fileNum )
3084{
3085    return tr_strdup_printf( "%s.part", tor->info.files[fileNum].name );
3086}
Note: See TracBrowser for help on using the repository browser.