source: trunk/libtransmission/torrent.c @ 11813

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

(trunk libT) #3973 "JIT verification verifies freshly-downloaded torrents" -- test fix.

Files downloaded in Transmission 2.20 betas [1..3] forced each piece to be checked twice -- once on download, and once when uploading the piece for the first time. Older versions of Transmission didn't perform the latter check unless the file had changed after it was downloaded. This commit restores that behavior.

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