source: trunk/libtransmission/torrent.c @ 9567

Last change on this file since 9567 was 9567, checked in by charles, 12 years ago

(trunk libT) fix the resume files' "percent done" bug that was introduced yesterday with the magnet link commit

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