source: trunk/libtransmission/torrent.c @ 11535

Last change on this file since 11535 was 11535, checked in by charles, 11 years ago

(trunk libT) #3764 "script-torrent-done shouldn't block libtransmission" -- add the "calling script ..." line back into the logfile.

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