source: trunk/libtransmission/torrent.c @ 11534

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

(trunk libT) #3764 "script-torrent-done shouldn't block libtransmission" -- fixed.

  • Property svn:keywords set to Date Rev Author Id
File size: 80.0 KB
Line 
1/*
2 * This file Copyright (C) 2009-2010 Mnemosyne LLC
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: torrent.c 11534 2010-12-15 04:35:41Z 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[] = { tr_strdup_printf( "TR_APP_VERSION=%s", SHORT_VERSION_STRING ),
1867                         tr_strdup_printf( "TR_TIME_LOCALTIME=%s", timeStr ),
1868                         tr_strdup_printf( "TR_TORRENT_DIR=%s", tor->currentDir ),
1869                         tr_strdup_printf( "TR_TORRENT_ID=%d", tr_torrentId( tor ) ),
1870                         tr_strdup_printf( "TR_TORRENT_HASH=%s", tor->info.hashString ),
1871                         tr_strdup_printf( "TR_TORRENT_NAME=%s", tr_torrentName( tor ) ),
1872                         NULL };
1873        if( !fork( ) )
1874        {
1875            execve( script, cmd, env );
1876            tr_torerr( tor, _( "Script failed with errno %d (%s)" ), errno, strerror(errno) );
1877            _exit( 0 );
1878        }
1879    }
1880}
1881
1882void
1883tr_torrentRecheckCompleteness( tr_torrent * tor )
1884{
1885    tr_completeness completeness;
1886
1887    assert( tr_isTorrent( tor ) );
1888
1889    tr_torrentLock( tor );
1890
1891    completeness = tr_cpGetStatus( &tor->completion );
1892
1893    if( completeness != tor->completeness )
1894    {
1895        const int recentChange = tor->downloadedCur != 0;
1896        const tr_bool wasLeeching = !tr_torrentIsSeed( tor );
1897        const tr_bool wasRunning = tor->isRunning;
1898
1899        if( recentChange )
1900        {
1901            tr_torinf( tor, _( "State changed from \"%1$s\" to \"%2$s\"" ),
1902                      getCompletionString( tor->completeness ),
1903                      getCompletionString( completeness ) );
1904        }
1905
1906        tor->completeness = completeness;
1907        tr_fdTorrentClose( tor->session, tor->uniqueId );
1908
1909        if( tr_torrentIsSeed( tor ) )
1910        {
1911            if( recentChange )
1912            {
1913                tr_announcerTorrentCompleted( tor );
1914                tor->doneDate = tor->anyDate = tr_time( );
1915            }
1916
1917            if( wasLeeching && wasRunning )
1918            {
1919                /* clear interested flag on all peers */
1920                tr_peerMgrClearInterest( tor );
1921
1922                /* if completeness was TR_LEECH then the seed limit check will have been skipped in bandwidthPulse */
1923                tr_torrentCheckSeedLimit( tor );
1924            }
1925
1926            if( tor->currentDir == tor->incompleteDir )
1927                tr_torrentSetLocation( tor, tor->downloadDir, TRUE, NULL, NULL );
1928
1929            if( tr_sessionIsTorrentDoneScriptEnabled( tor->session ) )
1930                torrentCallScript( tor, tr_sessionGetTorrentDoneScript( tor->session ) );
1931        }
1932
1933        fireCompletenessChange( tor, wasRunning, completeness );
1934
1935        tr_torrentSetDirty( tor );
1936    }
1937
1938    tr_torrentUnlock( tor );
1939}
1940
1941/***
1942****
1943***/
1944
1945static void
1946tr_torrentFireMetadataCompleted( tr_torrent * tor )
1947{
1948    assert( tr_isTorrent( tor ) );
1949
1950    if( tor->metadata_func )
1951        tor->metadata_func( tor, tor->metadata_func_user_data );
1952}
1953
1954void
1955tr_torrentSetMetadataCallback( tr_torrent                * tor,
1956                               tr_torrent_metadata_func    func,
1957                               void                      * user_data )
1958{
1959    assert( tr_isTorrent( tor ) );
1960
1961    tor->metadata_func = func;
1962    tor->metadata_func_user_data = user_data;
1963}
1964
1965
1966/**
1967***  File priorities
1968**/
1969
1970void
1971tr_torrentInitFilePriority( tr_torrent *    tor,
1972                            tr_file_index_t fileIndex,
1973                            tr_priority_t   priority )
1974{
1975    tr_piece_index_t i;
1976    tr_file *        file;
1977
1978    assert( tr_isTorrent( tor ) );
1979    assert( fileIndex < tor->info.fileCount );
1980    assert( tr_isPriority( priority ) );
1981
1982    file = &tor->info.files[fileIndex];
1983    file->priority = priority;
1984    for( i = file->firstPiece; i <= file->lastPiece; ++i )
1985        tor->info.pieces[i].priority = calculatePiecePriority( tor, i, fileIndex );
1986}
1987
1988void
1989tr_torrentSetFilePriorities( tr_torrent             * tor,
1990                             const tr_file_index_t  * files,
1991                             tr_file_index_t          fileCount,
1992                             tr_priority_t            priority )
1993{
1994    tr_file_index_t i;
1995    assert( tr_isTorrent( tor ) );
1996    tr_torrentLock( tor );
1997
1998    for( i = 0; i < fileCount; ++i )
1999        if( files[i] < tor->info.fileCount )
2000            tr_torrentInitFilePriority( tor, files[i], priority );
2001    tr_torrentSetDirty( tor );
2002    tr_peerMgrRebuildRequests( tor );
2003
2004    tr_torrentUnlock( tor );
2005}
2006
2007tr_priority_t*
2008tr_torrentGetFilePriorities( const tr_torrent * tor )
2009{
2010    tr_file_index_t i;
2011    tr_priority_t * p;
2012
2013    assert( tr_isTorrent( tor ) );
2014
2015    tr_torrentLock( tor );
2016    p = tr_new0( tr_priority_t, tor->info.fileCount );
2017    for( i = 0; i < tor->info.fileCount; ++i )
2018        p[i] = tor->info.files[i].priority;
2019    tr_torrentUnlock( tor );
2020
2021    return p;
2022}
2023
2024/**
2025***  File DND
2026**/
2027
2028static void
2029setFileDND( tr_torrent * tor, tr_file_index_t fileIndex, int doDownload )
2030{
2031    tr_file *        file;
2032    const int8_t     dnd = !doDownload;
2033    tr_piece_index_t firstPiece;
2034    int8_t           firstPieceDND;
2035    tr_piece_index_t lastPiece;
2036    int8_t           lastPieceDND;
2037    tr_file_index_t  i;
2038
2039    assert( tr_isTorrent( tor ) );
2040
2041    file = &tor->info.files[fileIndex];
2042    file->dnd = dnd;
2043    firstPiece = file->firstPiece;
2044    lastPiece = file->lastPiece;
2045
2046    /* can't set the first piece to DND unless
2047       every file using that piece is DND */
2048    firstPieceDND = dnd;
2049    if( fileIndex > 0 )
2050    {
2051        for( i = fileIndex - 1; firstPieceDND; --i )
2052        {
2053            if( tor->info.files[i].lastPiece != firstPiece )
2054                break;
2055            firstPieceDND = tor->info.files[i].dnd;
2056            if( !i )
2057                break;
2058        }
2059    }
2060
2061    /* can't set the last piece to DND unless
2062       every file using that piece is DND */
2063    lastPieceDND = dnd;
2064    for( i = fileIndex + 1; lastPieceDND && i < tor->info.fileCount; ++i )
2065    {
2066        if( tor->info.files[i].firstPiece != lastPiece )
2067            break;
2068        lastPieceDND = tor->info.files[i].dnd;
2069    }
2070
2071    if( firstPiece == lastPiece )
2072    {
2073        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
2074    }
2075    else
2076    {
2077        tr_piece_index_t pp;
2078        tor->info.pieces[firstPiece].dnd = firstPieceDND;
2079        tor->info.pieces[lastPiece].dnd = lastPieceDND;
2080        for( pp = firstPiece + 1; pp < lastPiece; ++pp )
2081            tor->info.pieces[pp].dnd = dnd;
2082    }
2083}
2084
2085void
2086tr_torrentInitFileDLs( tr_torrent             * tor,
2087                       const tr_file_index_t  * files,
2088                       tr_file_index_t          fileCount,
2089                       tr_bool                  doDownload )
2090{
2091    tr_file_index_t i;
2092
2093    assert( tr_isTorrent( tor ) );
2094
2095    tr_torrentLock( tor );
2096
2097    for( i=0; i<fileCount; ++i )
2098        if( files[i] < tor->info.fileCount )
2099            setFileDND( tor, files[i], doDownload );
2100
2101    tr_cpInvalidateDND( &tor->completion );
2102
2103    tr_torrentUnlock( tor );
2104}
2105
2106void
2107tr_torrentSetFileDLs( tr_torrent             * tor,
2108                      const tr_file_index_t  * files,
2109                      tr_file_index_t          fileCount,
2110                      tr_bool                  doDownload )
2111{
2112    assert( tr_isTorrent( tor ) );
2113    tr_torrentLock( tor );
2114
2115    tr_torrentInitFileDLs( tor, files, fileCount, doDownload );
2116    tr_torrentSetDirty( tor );
2117    tr_peerMgrRebuildRequests( tor );
2118
2119    tr_torrentUnlock( tor );
2120}
2121
2122/***
2123****
2124***/
2125
2126tr_priority_t
2127tr_torrentGetPriority( const tr_torrent * tor )
2128{
2129    assert( tr_isTorrent( tor ) );
2130
2131    return tor->bandwidth->priority;
2132}
2133
2134void
2135tr_torrentSetPriority( tr_torrent * tor, tr_priority_t priority )
2136{
2137    assert( tr_isTorrent( tor ) );
2138    assert( tr_isPriority( priority ) );
2139
2140    if( tor->bandwidth->priority != priority )
2141    {
2142        tor->bandwidth->priority = priority;
2143
2144        tr_torrentSetDirty( tor );
2145    }
2146}
2147
2148/***
2149****
2150***/
2151
2152void
2153tr_torrentSetPeerLimit( tr_torrent * tor,
2154                        uint16_t     maxConnectedPeers )
2155{
2156    assert( tr_isTorrent( tor ) );
2157
2158    if ( tor->maxConnectedPeers != maxConnectedPeers )
2159    {
2160        tor->maxConnectedPeers = maxConnectedPeers;
2161
2162        tr_torrentSetDirty( tor );
2163    }
2164}
2165
2166uint16_t
2167tr_torrentGetPeerLimit( const tr_torrent * tor )
2168{
2169    assert( tr_isTorrent( tor ) );
2170
2171    return tor->maxConnectedPeers;
2172}
2173
2174/***
2175****
2176***/
2177
2178tr_block_index_t
2179_tr_block( const tr_torrent * tor,
2180           tr_piece_index_t   index,
2181           uint32_t           offset )
2182{
2183    tr_block_index_t ret;
2184
2185    assert( tr_isTorrent( tor ) );
2186
2187    ret = index;
2188    ret *= ( tor->info.pieceSize / tor->blockSize );
2189    ret += offset / tor->blockSize;
2190    return ret;
2191}
2192
2193tr_bool
2194tr_torrentReqIsValid( const tr_torrent * tor,
2195                      tr_piece_index_t   index,
2196                      uint32_t           offset,
2197                      uint32_t           length )
2198{
2199    int err = 0;
2200
2201    assert( tr_isTorrent( tor ) );
2202
2203    if( index >= tor->info.pieceCount )
2204        err = 1;
2205    else if( length < 1 )
2206        err = 2;
2207    else if( ( offset + length ) > tr_torPieceCountBytes( tor, index ) )
2208        err = 3;
2209    else if( length > MAX_BLOCK_SIZE )
2210        err = 4;
2211    else if( tr_pieceOffset( tor, index, offset, length ) > tor->info.totalSize )
2212        err = 5;
2213
2214    if( err ) tr_tordbg( tor, "index %lu offset %lu length %lu err %d\n",
2215                              (unsigned long)index,
2216                              (unsigned long)offset,
2217                              (unsigned long)length,
2218                              err );
2219
2220    return !err;
2221}
2222
2223uint64_t
2224tr_pieceOffset( const tr_torrent * tor,
2225                tr_piece_index_t   index,
2226                uint32_t           offset,
2227                uint32_t           length )
2228{
2229    uint64_t ret;
2230
2231    assert( tr_isTorrent( tor ) );
2232
2233    ret = tor->info.pieceSize;
2234    ret *= index;
2235    ret += offset;
2236    ret += length;
2237    return ret;
2238}
2239
2240/***
2241****
2242***/
2243
2244void
2245tr_torrentSetPieceChecked( tr_torrent * tor, tr_piece_index_t pieceIndex )
2246{
2247    assert( tr_isTorrent( tor ) );
2248    assert( pieceIndex < tor->info.pieceCount );
2249
2250    tr_tordbg( tor, "[LAZY] setting piece %zu timeChecked to now", (size_t)pieceIndex );
2251    tor->info.pieces[pieceIndex].timeChecked = tr_time( );
2252}
2253
2254void
2255tr_torrentSetChecked( tr_torrent * tor, time_t when )
2256{
2257    tr_piece_index_t i, n;
2258
2259    assert( tr_isTorrent( tor ) );
2260
2261    for( i=0, n=tor->info.pieceCount; i!=n; ++i )
2262        tor->info.pieces[i].timeChecked = when;
2263}
2264
2265tr_bool
2266tr_torrentCheckPiece( tr_torrent * tor, tr_piece_index_t pieceIndex )
2267{
2268    const tr_bool pass = tr_ioTestPiece( tor, pieceIndex );
2269
2270    tr_tordbg( tor, "[LAZY] tr_torrentCheckPiece tested piece %zu, pass==%d", (size_t)pieceIndex, (int)pass );
2271    tr_torrentSetHasPiece( tor, pieceIndex, pass );
2272    tr_torrentSetPieceChecked( tor, pieceIndex );
2273    tor->anyDate = tr_time( );
2274    tr_torrentSetDirty( tor );
2275
2276    return pass;
2277}
2278
2279static time_t
2280getFileMTime( const tr_torrent * tor, tr_file_index_t i )
2281{
2282    struct stat sb;
2283    time_t mtime = 0;
2284    char * path = tr_torrentFindFile( tor, i );
2285
2286    if( ( path != NULL ) && !stat( path, &sb ) && S_ISREG( sb.st_mode ) )
2287    {
2288#ifdef SYS_DARWIN
2289        mtime = sb.st_mtimespec.tv_sec;
2290#else
2291        mtime = sb.st_mtime;
2292#endif
2293    }
2294
2295    tr_free( path );
2296    return mtime;
2297}
2298
2299tr_bool
2300tr_torrentPieceNeedsCheck( const tr_torrent * tor, tr_piece_index_t p )
2301{
2302    uint64_t unused;
2303    tr_file_index_t f;
2304    const tr_info * inf = tr_torrentInfo( tor );
2305
2306    /* if we've never checked this piece, then it needs to be checked */
2307    if( !inf->pieces[p].timeChecked ) {
2308        tr_tordbg( tor, "[LAZY] piece %zu needs to be tested because it's never been tested", (size_t)p );
2309        return TRUE;
2310    }
2311
2312    /* If we think we've completed one of the files in this piece,
2313     * but it's been modified since we last checked it,
2314     * then it needs to be rechecked */
2315    tr_ioFindFileLocation( tor, p, 0, &f, &unused );
2316    for( ; f < inf->fileCount && pieceHasFile( p, &inf->files[f] ); ++f ) {
2317        if( tr_cpFileIsComplete( &tor->completion, f ) ) {
2318            if( getFileMTime( tor, f ) > inf->pieces[p].timeChecked ) {
2319                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 );
2320                return TRUE;
2321            }
2322        }
2323    }
2324
2325    tr_tordbg( tor, "[LAZY] piece %zu does not need to be tested", (size_t)p );
2326    return FALSE;
2327}
2328
2329/***
2330****
2331***/
2332
2333static int
2334compareTrackerByTier( const void * va, const void * vb )
2335{
2336    const tr_tracker_info * a = va;
2337    const tr_tracker_info * b = vb;
2338
2339    /* sort by tier */
2340    if( a->tier != b->tier )
2341        return a->tier - b->tier;
2342
2343    /* get the effects of a stable sort by comparing the two elements' addresses */
2344    return a - b;
2345}
2346
2347tr_bool
2348tr_torrentSetAnnounceList( tr_torrent             * tor,
2349                           const tr_tracker_info  * trackers_in,
2350                           int                      trackerCount )
2351{
2352    int i;
2353    tr_benc metainfo;
2354    tr_bool ok = TRUE;
2355    tr_tracker_info * trackers;
2356
2357    tr_torrentLock( tor );
2358
2359    assert( tr_isTorrent( tor ) );
2360
2361    /* ensure the trackers' tiers are in ascending order */
2362    trackers = tr_memdup( trackers_in, sizeof( tr_tracker_info ) * trackerCount );
2363    qsort( trackers, trackerCount, sizeof( tr_tracker_info ), compareTrackerByTier );
2364
2365    /* look for bad URLs */
2366    for( i=0; ok && i<trackerCount; ++i )
2367        if( !tr_urlIsValidTracker( trackers[i].announce ) )
2368            ok = FALSE;
2369
2370    /* save to the .torrent file */
2371    if( ok && !tr_bencLoadFile( &metainfo, TR_FMT_BENC, tor->info.torrent ) )
2372    {
2373        tr_bool hasInfo;
2374        tr_info tmpInfo;
2375
2376        /* remove the old fields */
2377        tr_bencDictRemove( &metainfo, "announce" );
2378        tr_bencDictRemove( &metainfo, "announce-list" );
2379
2380        /* add the new fields */
2381        if( trackerCount > 0 )
2382        {
2383            tr_bencDictAddStr( &metainfo, "announce", trackers[0].announce );
2384        }
2385        if( trackerCount > 1 )
2386        {
2387            int i;
2388            int prevTier = -1;
2389            tr_benc * tier = NULL;
2390            tr_benc * announceList = tr_bencDictAddList( &metainfo, "announce-list", 0 );
2391
2392            for( i=0; i<trackerCount; ++i ) {
2393                if( prevTier != trackers[i].tier ) {
2394                    prevTier = trackers[i].tier;
2395                    tier = tr_bencListAddList( announceList, 0 );
2396                }
2397                tr_bencListAddStr( tier, trackers[i].announce );
2398            }
2399        }
2400
2401        /* try to parse it back again, to make sure it's good */
2402        memset( &tmpInfo, 0, sizeof( tr_info ) );
2403        if( tr_metainfoParse( tor->session, &metainfo, &tmpInfo,
2404                              &hasInfo, &tor->infoDictLength ) )
2405        {
2406            /* it's good, so keep these new trackers and free the old ones */
2407
2408            tr_info swap;
2409            swap.trackers = tor->info.trackers;
2410            swap.trackerCount = tor->info.trackerCount;
2411            tor->info.trackers = tmpInfo.trackers;
2412            tor->info.trackerCount = tmpInfo.trackerCount;
2413            tmpInfo.trackers = swap.trackers;
2414            tmpInfo.trackerCount = swap.trackerCount;
2415
2416            tr_metainfoFree( &tmpInfo );
2417            tr_bencToFile( &metainfo, TR_FMT_BENC, tor->info.torrent );
2418        }
2419
2420        /* cleanup */
2421        tr_bencFree( &metainfo );
2422
2423        /* if we had a tracker-related error on this torrent,
2424         * and that tracker's been removed,
2425         * then clear the error */
2426        if(    ( tor->error == TR_STAT_TRACKER_WARNING )
2427            || ( tor->error == TR_STAT_TRACKER_ERROR ) )
2428        {
2429            tr_bool clear = TRUE;
2430
2431            for( i=0; clear && i<trackerCount; ++i )
2432                if( !strcmp( trackers[i].announce, tor->errorTracker ) )
2433                    clear = FALSE;
2434
2435            if( clear )
2436                tr_torrentClearError( tor );
2437        }
2438
2439        /* tell the announcer to reload this torrent's tracker list */
2440        tr_announcerResetTorrent( tor->session->announcer, tor );
2441    }
2442
2443    tr_torrentUnlock( tor );
2444
2445    tr_free( trackers );
2446    return ok;
2447}
2448
2449/**
2450***
2451**/
2452
2453void
2454tr_torrentSetAddedDate( tr_torrent * tor,
2455                        time_t       t )
2456{
2457    assert( tr_isTorrent( tor ) );
2458
2459    tor->addedDate = t;
2460    tor->anyDate = MAX( tor->anyDate, tor->addedDate );
2461}
2462
2463void
2464tr_torrentSetActivityDate( tr_torrent * tor, time_t t )
2465{
2466    assert( tr_isTorrent( tor ) );
2467
2468    tor->activityDate = t;
2469    tor->anyDate = MAX( tor->anyDate, tor->activityDate );
2470}
2471
2472void
2473tr_torrentSetDoneDate( tr_torrent * tor,
2474                       time_t       t )
2475{
2476    assert( tr_isTorrent( tor ) );
2477
2478    tor->doneDate = t;
2479    tor->anyDate = MAX( tor->anyDate, tor->doneDate );
2480}
2481
2482/**
2483***
2484**/
2485
2486uint64_t
2487tr_torrentGetBytesLeftToAllocate( const tr_torrent * tor )
2488{
2489    tr_file_index_t i;
2490    uint64_t bytesLeft = 0;
2491
2492    assert( tr_isTorrent( tor ) );
2493
2494    for( i=0; i<tor->info.fileCount; ++i )
2495    {
2496        if( !tor->info.files[i].dnd )
2497        {
2498            struct stat sb;
2499            const uint64_t length = tor->info.files[i].length;
2500            char * path = tr_torrentFindFile( tor, i );
2501
2502            bytesLeft += length;
2503
2504            if( ( path != NULL ) && !stat( path, &sb )
2505                                 && S_ISREG( sb.st_mode )
2506                                 && ( (uint64_t)sb.st_size <= length ) )
2507                bytesLeft -= sb.st_size;
2508
2509            tr_free( path );
2510        }
2511    }
2512
2513    return bytesLeft;
2514}
2515
2516/****
2517*****  Removing the torrent's local data
2518****/
2519
2520static int
2521vstrcmp( const void * a, const void * b )
2522{
2523    return strcmp( a, b );
2524}
2525
2526static int
2527compareLongestFirst( const void * a, const void * b )
2528{
2529    const size_t alen = strlen( a );
2530    const size_t blen = strlen( b );
2531
2532    if( alen != blen )
2533        return alen > blen ? -1 : 1;
2534
2535    return vstrcmp( a, b );
2536}
2537
2538static void
2539addDirtyFile( const char  * root,
2540              const char  * filename,
2541              tr_ptrArray * dirtyFolders )
2542{
2543    char * dir = tr_dirname( filename );
2544
2545    /* add the parent folders to dirtyFolders until we reach the root or a known-dirty */
2546    while (     ( dir != NULL )
2547             && ( strlen( root ) <= strlen( dir ) )
2548             && ( tr_ptrArrayFindSorted( dirtyFolders, dir, vstrcmp ) == NULL ) )
2549    {
2550        char * tmp;
2551        tr_ptrArrayInsertSorted( dirtyFolders, tr_strdup( dir ), vstrcmp );
2552
2553        tmp = tr_dirname( dir );
2554        tr_free( dir );
2555        dir = tmp;
2556    }
2557
2558    tr_free( dir );
2559}
2560
2561static void
2562walkLocalData( const tr_torrent * tor,
2563               const char       * root,
2564               const char       * dir,
2565               const char       * base,
2566               tr_ptrArray      * torrentFiles,
2567               tr_ptrArray      * folders,
2568               tr_ptrArray      * dirtyFolders )
2569{
2570    int i;
2571    struct stat sb;
2572    char * buf;
2573
2574    assert( tr_isTorrent( tor ) );
2575
2576    buf = tr_buildPath( dir, base, NULL );
2577    i = stat( buf, &sb );
2578    if( !i )
2579    {
2580        DIR * odir = NULL;
2581
2582        if( S_ISDIR( sb.st_mode ) && ( ( odir = opendir ( buf ) ) ) )
2583        {
2584            struct dirent *d;
2585            tr_ptrArrayInsertSorted( folders, tr_strdup( buf ), vstrcmp );
2586            for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
2587                if( d->d_name && strcmp( d->d_name, "." ) && strcmp( d->d_name, ".." ) )
2588                    walkLocalData( tor, root, buf, d->d_name, torrentFiles, folders, dirtyFolders );
2589            closedir( odir );
2590        }
2591        else if( S_ISREG( sb.st_mode ) && ( sb.st_size > 0 ) )
2592        {
2593            const char * sub = buf + strlen( tor->currentDir ) + strlen( TR_PATH_DELIMITER_STR );
2594            const tr_bool isTorrentFile = tr_ptrArrayFindSorted( torrentFiles, sub, vstrcmp ) != NULL;
2595            if( !isTorrentFile )
2596                addDirtyFile( root, buf, dirtyFolders );
2597        }
2598    }
2599
2600    tr_free( buf );
2601}
2602
2603static void
2604deleteLocalFile( const char * filename, tr_fileFunc fileFunc )
2605{
2606    struct stat sb;
2607    if( !stat( filename, &sb ) ) /* if file exists... */
2608        fileFunc( filename );
2609}
2610
2611static void
2612deleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
2613{
2614    int i, n;
2615    char ** s;
2616    tr_file_index_t f;
2617    tr_ptrArray torrentFiles = TR_PTR_ARRAY_INIT;
2618    tr_ptrArray folders      = TR_PTR_ARRAY_INIT;
2619    tr_ptrArray dirtyFolders = TR_PTR_ARRAY_INIT; /* dirty == contains non-torrent files */
2620
2621    const char * firstFile = tor->info.files[0].name;
2622    const char * cpch = strchr( firstFile, TR_PATH_DELIMITER );
2623    char * tmp = cpch ? tr_strndup( firstFile, cpch - firstFile ) : NULL;
2624    char * root = tr_buildPath( tor->currentDir, tmp, NULL );
2625
2626    assert( tr_isTorrent( tor ) );
2627
2628    for( f=0; f<tor->info.fileCount; ++f ) {
2629        tr_ptrArrayInsertSorted( &torrentFiles, tr_strdup( tor->info.files[f].name ), vstrcmp );
2630        tr_ptrArrayInsertSorted( &torrentFiles, tr_torrentBuildPartial( tor, f ), vstrcmp );
2631    }
2632
2633    /* build the set of folders and dirtyFolders */
2634    walkLocalData( tor, root, root, NULL, &torrentFiles, &folders, &dirtyFolders );
2635
2636    /* try to remove entire folders first, so that the recycle bin will be tidy */
2637    s = (char**) tr_ptrArrayPeek( &folders, &n );
2638    for( i=0; i<n; ++i )
2639        if( tr_ptrArrayFindSorted( &dirtyFolders, s[i], vstrcmp ) == NULL )
2640            deleteLocalFile( s[i], fileFunc );
2641
2642    /* now blow away any remaining torrent files, such as torrent files in dirty folders */
2643    for( i=0, n=tr_ptrArraySize( &torrentFiles ); i<n; ++i ) {
2644        char * path = tr_buildPath( tor->currentDir, tr_ptrArrayNth( &torrentFiles, i ), NULL );
2645        deleteLocalFile( path, fileFunc );
2646        tr_free( path );
2647    }
2648
2649    /* Now clean out the directories left empty from the previous step.
2650     * Work from deepest to shallowest s.t. lower folders
2651     * won't prevent the upper folders from being deleted */
2652    {
2653        tr_ptrArray cleanFolders = TR_PTR_ARRAY_INIT;
2654        s = (char**) tr_ptrArrayPeek( &folders, &n );
2655        for( i=0; i<n; ++i )
2656            if( tr_ptrArrayFindSorted( &dirtyFolders, s[i], vstrcmp ) == NULL )
2657                tr_ptrArrayInsertSorted( &cleanFolders, s[i], compareLongestFirst );
2658        s = (char**) tr_ptrArrayPeek( &cleanFolders, &n );
2659        for( i=0; i<n; ++i ) {
2660#ifdef SYS_DARWIN
2661            char * dsStore = tr_buildPath( s[i], ".DS_Store", NULL );
2662            deleteLocalFile( dsStore, fileFunc );
2663            tr_free( dsStore );
2664#endif
2665            deleteLocalFile( s[i], fileFunc );
2666        }
2667        tr_ptrArrayDestruct( &cleanFolders, NULL );
2668    }
2669
2670    /* cleanup */
2671    tr_ptrArrayDestruct( &dirtyFolders, tr_free );
2672    tr_ptrArrayDestruct( &folders, tr_free );
2673    tr_ptrArrayDestruct( &torrentFiles, tr_free );
2674    tr_free( root );
2675    tr_free( tmp );
2676}
2677
2678void
2679tr_torrentDeleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
2680{
2681    assert( tr_isTorrent( tor ) );
2682
2683    if( fileFunc == NULL )
2684        fileFunc = remove;
2685
2686    /* close all the files because we're about to delete them */
2687    tr_fdTorrentClose( tor->session, tor->uniqueId );
2688
2689    if( tor->info.fileCount > 1 )
2690    {
2691        deleteLocalData( tor, fileFunc );
2692    }
2693    else if( tor->info.fileCount == 1 )
2694    {
2695        char * tmp;
2696
2697        /* torrent only has one file */
2698        char * path = tr_buildPath( tor->currentDir, tor->info.files[0].name, NULL );
2699        deleteLocalFile( path, fileFunc );
2700        tr_free( path );
2701
2702        tmp = tr_torrentBuildPartial( tor, 0 );
2703        path = tr_buildPath( tor->currentDir, tmp, NULL );
2704        deleteLocalFile( path, fileFunc );
2705        tr_free( path );
2706        tr_free( tmp );
2707    }
2708}
2709
2710/***
2711****
2712***/
2713
2714struct LocationData
2715{
2716    tr_bool move_from_old_location;
2717    int * setme_state;
2718    double * setme_progress;
2719    char * location;
2720    tr_torrent * tor;
2721};
2722
2723static tr_bool
2724isSameLocation( const char * path1, const char * path2 )
2725{
2726    struct stat s1, s2;
2727    const int err1 = stat( path1, &s1 );
2728    const int err2 = stat( path2, &s2 );
2729
2730    if( !err1 && !err2 ) {
2731        tr_dbg( "path1 dev:inode is %"PRIu64":%"PRIu64"; "
2732                "path2 dev:inode is %"PRIu64":%"PRIu64,
2733                (uint64_t)s1.st_dev, (uint64_t)s1.st_ino,
2734                (uint64_t)s2.st_dev, (uint64_t)s2.st_ino );
2735        return ( s1.st_dev == s2.st_dev )
2736            && ( s1.st_ino == s2.st_ino );
2737    }
2738
2739    /* either one, or the other, or both don't exist... */
2740    tr_dbg( "stat(%s) returned %d\n", path1, err1 );
2741    tr_dbg( "stat(%s) returned %d\n", path2, err2 );
2742    return FALSE;
2743}
2744
2745static void
2746setLocation( void * vdata )
2747{
2748    tr_bool err = FALSE;
2749    struct LocationData * data = vdata;
2750    tr_torrent * tor = data->tor;
2751    const tr_bool do_move = data->move_from_old_location;
2752    const char * location = data->location;
2753    double bytesHandled = 0;
2754
2755    assert( tr_isTorrent( tor ) );
2756
2757    tr_dbg( "Moving \"%s\" location from currentDir \"%s\" to \"%s\"",
2758            tr_torrentName(tor), tor->currentDir, location );
2759
2760    tr_mkdirp( location, 0777 );
2761
2762    if( !isSameLocation( location, tor->currentDir ) )
2763    {
2764        tr_file_index_t i;
2765
2766        /* bad idea to move files while they're being verified... */
2767        tr_verifyRemove( tor );
2768
2769        /* try to move the files.
2770         * FIXME: there are still all kinds of nasty cases, like what
2771         * if the target directory runs out of space halfway through... */
2772        for( i=0; !err && i<tor->info.fileCount; ++i )
2773        {
2774            const tr_file * f = &tor->info.files[i];
2775            const char * oldbase;
2776            char * sub;
2777            if( tr_torrentFindFile2( tor, i, &oldbase, &sub ) )
2778            {
2779                char * oldpath = tr_buildPath( oldbase, sub, NULL );
2780                char * newpath = tr_buildPath( location, sub, NULL );
2781
2782                tr_dbg( "Found file #%d: %s", (int)i, oldpath );
2783
2784                if( do_move )
2785                {
2786                    tr_bool renamed = FALSE;
2787                    errno = 0;
2788                    tr_torinf( tor, "moving \"%s\" to \"%s\"", oldpath, newpath );
2789                    if( tr_moveFile( oldpath, newpath, &renamed ) )
2790                    {
2791                        err = TRUE;
2792                        tr_torerr( tor, "error moving \"%s\" to \"%s\": %s",
2793                                        oldpath, newpath, tr_strerror( errno ) );
2794                    }
2795                }
2796
2797                tr_free( newpath );
2798                tr_free( oldpath );
2799                tr_free( sub );
2800            }
2801
2802            if( data->setme_progress )
2803            {
2804                bytesHandled += f->length;
2805                *data->setme_progress = bytesHandled / tor->info.totalSize;
2806            }
2807        }
2808
2809        if( !err )
2810        {
2811            /* blow away the leftover subdirectories in the old location */
2812            if( do_move )
2813                tr_torrentDeleteLocalData( tor, remove );
2814
2815            /* set the new location and reverify */
2816            tr_torrentSetDownloadDir( tor, location );
2817        }
2818    }
2819
2820    if( !err && do_move )
2821    {
2822        tr_free( tor->incompleteDir );
2823        tor->incompleteDir = NULL;
2824        tor->currentDir = tor->downloadDir;
2825    }
2826
2827    if( data->setme_state )
2828        *data->setme_state = err ? TR_LOC_ERROR : TR_LOC_DONE;
2829
2830    /* cleanup */
2831    tr_free( data->location );
2832    tr_free( data );
2833}
2834
2835void
2836tr_torrentSetLocation( tr_torrent  * tor,
2837                       const char  * location,
2838                       tr_bool       move_from_old_location,
2839                       double      * setme_progress,
2840                       int         * setme_state )
2841{
2842    struct LocationData * data;
2843
2844    assert( tr_isTorrent( tor ) );
2845
2846    if( setme_state )
2847        *setme_state = TR_LOC_MOVING;
2848    if( setme_progress )
2849        *setme_progress = 0;
2850
2851    /* run this in the libtransmission thread */
2852    data = tr_new( struct LocationData, 1 );
2853    data->tor = tor;
2854    data->location = tr_strdup( location );
2855    data->move_from_old_location = move_from_old_location;
2856    data->setme_state = setme_state;
2857    data->setme_progress = setme_progress;
2858    tr_runInEventThread( tor->session, setLocation, data );
2859}
2860
2861/***
2862****
2863***/
2864
2865void
2866tr_torrentFileCompleted( tr_torrent * tor, tr_file_index_t fileNum )
2867{
2868    char * sub;
2869    const char * base;
2870
2871    /* close the file so that we can reopen in read-only mode as needed */
2872    tr_fdFileClose( tor->session, tor, fileNum );
2873
2874    /* if the torrent's current filename isn't the same as the one in the
2875     * metadata -- for example, if it had the ".part" suffix appended to
2876     * it until now -- then rename it to match the one in the metadata */
2877    if( tr_torrentFindFile2( tor, fileNum, &base, &sub ) )
2878    {
2879        const tr_file * file = &tor->info.files[fileNum];
2880
2881        if( strcmp( sub, file->name ) )
2882        {
2883            char * oldpath = tr_buildPath( base, sub, NULL );
2884            char * newpath = tr_buildPath( base, file->name, NULL );
2885
2886            if( rename( oldpath, newpath ) )
2887                tr_torerr( tor, "Error moving \"%s\" to \"%s\": %s", oldpath, newpath, tr_strerror( errno ) );
2888
2889            tr_free( newpath );
2890            tr_free( oldpath );
2891        }
2892
2893        tr_free( sub );
2894    }
2895}
2896
2897/***
2898****
2899***/
2900
2901static tr_bool
2902fileExists( const char * filename )
2903{
2904    struct stat sb;
2905    const tr_bool ok = !stat( filename, &sb );
2906    return ok;
2907}
2908
2909tr_bool
2910tr_torrentFindFile2( const tr_torrent * tor, tr_file_index_t fileNum,
2911                     const char ** base, char ** subpath )
2912{
2913    char * part;
2914    const tr_file * file;
2915    const char * b = NULL;
2916    const char * s = NULL;
2917
2918    assert( tr_isTorrent( tor ) );
2919    assert( fileNum < tor->info.fileCount );
2920
2921    file = &tor->info.files[fileNum];
2922    part = tr_torrentBuildPartial( tor, fileNum );
2923
2924    if( b == NULL ) {
2925        char * filename = tr_buildPath( tor->downloadDir, file->name, NULL );
2926        if( fileExists( filename ) ) {
2927            b = tor->downloadDir;
2928            s = file->name;
2929        }
2930        tr_free( filename );
2931    }
2932
2933    if( ( b == NULL ) && ( tor->incompleteDir != NULL ) ) {
2934        char * filename = tr_buildPath( tor->incompleteDir, file->name, NULL );
2935        if( fileExists( filename ) ) {
2936            b = tor->incompleteDir;
2937            s = file->name;
2938        }
2939        tr_free( filename );
2940    }
2941
2942    if( ( b == NULL ) && ( tor->incompleteDir != NULL ) ) {
2943        char * filename = tr_buildPath( tor->incompleteDir, part, NULL );
2944        if( fileExists( filename ) ) {
2945            b = tor->incompleteDir;
2946            s = part;
2947        }
2948        tr_free( filename );
2949    }
2950
2951    if( b == NULL) {
2952        char * filename = tr_buildPath( tor->downloadDir, part, NULL );
2953        if( fileExists( filename ) ) {
2954            b = tor->downloadDir;
2955            s = part;
2956        }
2957        tr_free( filename );
2958    }
2959
2960    if( base != NULL )
2961        *base = b;
2962    if( subpath != NULL )
2963        *subpath = tr_strdup( s );
2964
2965    tr_free( part );
2966    return b != NULL;
2967}
2968
2969char*
2970tr_torrentFindFile( const tr_torrent * tor, tr_file_index_t fileNum )
2971{
2972    char * subpath;
2973    char * ret = NULL;
2974    const char * base;
2975
2976    if( tr_torrentFindFile2( tor, fileNum, &base, &subpath ) )
2977    {
2978        ret = tr_buildPath( base, subpath, NULL );
2979        tr_free( subpath );
2980    }
2981
2982    return ret;
2983}
2984
2985/* Decide whether we should be looking for files in downloadDir or incompleteDir. */
2986static void
2987refreshCurrentDir( tr_torrent * tor )
2988{
2989    const char * dir = NULL;
2990
2991    if( tor->incompleteDir == NULL )
2992        dir = tor->downloadDir;
2993    else if( !tr_torrentHasMetadata( tor ) ) /* no files to find */
2994        dir = tor->incompleteDir;
2995    else if( !tr_torrentFindFile2( tor, 0, &dir, NULL ) )
2996        dir = tor->incompleteDir;
2997
2998    assert( dir != NULL );
2999    assert( ( dir == tor->downloadDir ) || ( dir == tor->incompleteDir ) );
3000    tor->currentDir = dir;
3001}
3002
3003char*
3004tr_torrentBuildPartial( const tr_torrent * tor, tr_file_index_t fileNum )
3005{
3006    return tr_strdup_printf( "%s.part", tor->info.files[fileNum].name );
3007}
Note: See TracBrowser for help on using the repository browser.