source: trunk/libtransmission/torrent.c @ 11522

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

(trunk) one of the periodic, banal "remove-trailing-spaces from lines of source code" cleanup commits

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