source: trunk/libtransmission/torrent.c @ 10437

Last change on this file since 10437 was 10437, checked in by livings124, 12 years ago

#1869 Move the finished state to libtransmission. This setting is now remembered between launches. This also causes torrents that hit the seed ratio to not have this setting changed to unlimited until start.

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