source: trunk/libtransmission/torrent.c @ 10247

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

(trunk libT) (1) reduce the default tracker announce timeout from 120 to 90 seconds. (2) for partial seeds, send an event=paused when we transition from being leeches to partial seeds, just as you would send an event=completed when becoming a seed

  • Property svn:keywords set to Date Rev Author Id
File size: 70.9 KB
Line 
1/*
2 * This file Copyright (C) 2009-2010 Mnemosyne LLC
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: torrent.c 10247 2010-02-20 21:03:49Z charles $
11 */
12
13#include <sys/types.h> /* stat */
14#include <sys/stat.h> /* stat */
15#include <unistd.h> /* stat */
16#include <dirent.h>
17
18#include <assert.h>
19#include <limits.h> /* INT_MAX */
20#include <math.h>
21#include <stdarg.h>
22#include <string.h> /* memcmp */
23#include <stdlib.h> /* qsort */
24
25#include <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( !strcmp( 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            tr_bencToFile( val, TR_FMT_BENC, path );
700            tr_sessionSetTorrentFile( tor->session, tor->info.hashString, path );
701        }
702    }
703
704    tor->tiers = tr_announcerAddTorrent( tor->session->announcer, tor );
705    tor->tiersSubscription = tr_announcerSubscribe( tor->tiers, onTrackerResponse, tor );
706
707    if( doStart )
708        torrentStart( tor );
709
710    tr_sessionUnlock( session );
711}
712
713static tr_parse_result
714torrentParseImpl( const tr_ctor * ctor, tr_info * setmeInfo,
715                  tr_bool * setmeHasInfo, int * dictOffset, int * dictLength )
716{
717    int             doFree;
718    tr_bool         didParse;
719    tr_bool         hasInfo = FALSE;
720    tr_info         tmp;
721    const tr_benc * metainfo;
722    tr_session    * session = tr_ctorGetSession( ctor );
723    tr_parse_result result = TR_PARSE_OK;
724
725    if( setmeInfo == NULL )
726        setmeInfo = &tmp;
727    memset( setmeInfo, 0, sizeof( tr_info ) );
728
729    if( tr_ctorGetMetainfo( ctor, &metainfo ) )
730        return TR_PARSE_ERR;
731
732    didParse = tr_metainfoParse( session, metainfo, setmeInfo,
733                                 &hasInfo, dictOffset, dictLength );
734    doFree = didParse && ( setmeInfo == &tmp );
735
736    if( !didParse )
737        result = TR_PARSE_ERR;
738
739    if( didParse && hasInfo && !getBlockSize( setmeInfo->pieceSize ) )
740        result = TR_PARSE_ERR;
741
742    if( didParse && session && tr_torrentExists( session, setmeInfo->hash ) )
743        result = TR_PARSE_DUPLICATE;
744
745    if( doFree )
746        tr_metainfoFree( setmeInfo );
747
748    if( setmeHasInfo != NULL )
749        *setmeHasInfo = hasInfo;
750
751    return result;
752}
753
754tr_parse_result
755tr_torrentParse( const tr_ctor * ctor, tr_info * setmeInfo )
756{
757    return torrentParseImpl( ctor, setmeInfo, NULL, NULL, NULL );
758}
759
760tr_torrent *
761tr_torrentNew( const tr_ctor * ctor, int * setmeError )
762{
763    int off, len;
764    tr_bool hasInfo;
765    tr_info tmpInfo;
766    tr_parse_result r;
767    tr_torrent * tor = NULL;
768
769    assert( ctor != NULL );
770    assert( tr_isSession( tr_ctorGetSession( ctor ) ) );
771
772    r = torrentParseImpl( ctor, &tmpInfo, &hasInfo, &off, &len );
773    if( r == TR_PARSE_OK )
774    {
775        tor = tr_new0( tr_torrent, 1 );
776        tor->info = tmpInfo;
777        if( hasInfo )
778        {
779            tor->infoDictOffset = off;
780            tor->infoDictLength = len;
781        }
782        torrentInit( tor, ctor );
783    }
784    else
785    {
786        if( r == TR_PARSE_DUPLICATE )
787            tr_metainfoFree( &tmpInfo );
788
789        if( setmeError )
790            *setmeError = r;
791    }
792
793    return tor;
794}
795
796/**
797***
798**/
799
800void
801tr_torrentSetDownloadDir( tr_torrent * tor, const char * path )
802{
803    assert( tr_isTorrent( tor  ) );
804
805    if( !path || !tor->downloadDir || strcmp( path, tor->downloadDir ) )
806    {
807        tr_free( tor->downloadDir );
808        tor->downloadDir = tr_strdup( path );
809        tr_torrentSetDirty( tor );
810    }
811
812    refreshCurrentDir( tor );
813}
814
815const char*
816tr_torrentGetDownloadDir( const tr_torrent * tor )
817{
818    assert( tr_isTorrent( tor  ) );
819
820    return tor->downloadDir;
821}
822
823const char *
824tr_torrentGetCurrentDir( const tr_torrent * tor )
825{
826    assert( tr_isTorrent( tor  ) );
827
828    return tor->currentDir;
829}
830
831
832void
833tr_torrentChangeMyPort( tr_torrent * tor )
834{
835    assert( tr_isTorrent( tor  ) );
836
837    tr_announcerChangeMyPort( tor );
838}
839
840static inline void
841tr_torrentManualUpdateImpl( void * vtor )
842{
843    tr_torrent * tor = vtor;
844
845    assert( tr_isTorrent( tor  ) );
846
847    if( tor->isRunning )
848        tr_announcerManualAnnounce( tor );
849}
850
851void
852tr_torrentManualUpdate( tr_torrent * tor )
853{
854    assert( tr_isTorrent( tor  ) );
855
856    tr_runInEventThread( tor->session, tr_torrentManualUpdateImpl, tor );
857}
858
859tr_bool
860tr_torrentCanManualUpdate( const tr_torrent * tor )
861{
862    return ( tr_isTorrent( tor  ) )
863        && ( tor->isRunning )
864        && ( tr_announcerCanManualAnnounce( tor ) );
865}
866
867const tr_info *
868tr_torrentInfo( const tr_torrent * tor )
869{
870    return tr_isTorrent( tor ) ? &tor->info : NULL;
871}
872
873const tr_stat *
874tr_torrentStatCached( tr_torrent * tor )
875{
876    const time_t now = tr_time( );
877
878    return tr_isTorrent( tor ) && ( now == tor->lastStatTime )
879         ? &tor->stats
880         : tr_torrentStat( tor );
881}
882
883void
884tr_torrentSetVerifyState( tr_torrent * tor, tr_verify_state state )
885{
886    assert( tr_isTorrent( tor ) );
887    assert( state==TR_VERIFY_NONE || state==TR_VERIFY_WAIT || state==TR_VERIFY_NOW );
888
889    tor->verifyState = state;
890    tor->anyDate = tr_time( );
891}
892
893tr_torrent_activity
894tr_torrentGetActivity( tr_torrent * tor )
895{
896    assert( tr_isTorrent( tor ) );
897
898    tr_torrentRecheckCompleteness( tor );
899
900    if( tor->verifyState == TR_VERIFY_NOW )
901        return TR_STATUS_CHECK;
902    if( tor->verifyState == TR_VERIFY_WAIT )
903        return TR_STATUS_CHECK_WAIT;
904    if( !tor->isRunning )
905        return TR_STATUS_STOPPED;
906    if( tor->completeness == TR_LEECH )
907        return TR_STATUS_DOWNLOAD;
908
909    return TR_STATUS_SEED;
910}
911
912const tr_stat *
913tr_torrentStat( tr_torrent * tor )
914{
915    tr_stat *               s;
916    int                     usableSeeds;
917    uint64_t                now;
918    double                  downloadedForRatio, seedRatio=0;
919    double                  d;
920    tr_bool                 checkSeedRatio;
921
922    if( !tor )
923        return NULL;
924
925    assert( tr_isTorrent( tor ) );
926    tr_torrentLock( tor );
927
928    tor->lastStatTime = tr_time( );
929
930    s = &tor->stats;
931    s->id = tor->uniqueId;
932    s->activity = tr_torrentGetActivity( tor );
933    s->error = tor->error;
934    memcpy( s->errorString, tor->errorString, sizeof( s->errorString ) );
935
936    s->manualAnnounceTime = tr_announcerNextManualAnnounce( tor );
937
938    tr_peerMgrTorrentStats( tor,
939                            &s->peersKnown,
940                            &s->peersConnected,
941                            &usableSeeds,
942                            &s->webseedsSendingToUs,
943                            &s->peersSendingToUs,
944                            &s->peersGettingFromUs,
945                            s->peersFrom );
946
947    now = tr_date( );
948    d = tr_peerMgrGetWebseedSpeed( tor, now );
949    s->rawUploadSpeed     = tr_bandwidthGetRawSpeed  ( tor->bandwidth, now, TR_UP );
950    s->pieceUploadSpeed   = tr_bandwidthGetPieceSpeed( tor->bandwidth, now, TR_UP );
951    s->rawDownloadSpeed   = d + tr_bandwidthGetRawSpeed  ( tor->bandwidth, now, TR_DOWN );
952    s->pieceDownloadSpeed = d + tr_bandwidthGetPieceSpeed( tor->bandwidth, now, TR_DOWN );
953
954    usableSeeds += tor->info.webseedCount;
955
956    s->percentComplete = tr_cpPercentComplete ( &tor->completion );
957    s->metadataPercentComplete = tr_torrentGetMetadataPercent( tor );
958
959    s->percentDone   = tr_cpPercentDone  ( &tor->completion );
960    s->leftUntilDone = tr_cpLeftUntilDone( &tor->completion );
961    s->sizeWhenDone  = tr_cpSizeWhenDone ( &tor->completion );
962
963    s->recheckProgress = s->activity == TR_STATUS_CHECK
964                       ? 1.0 -
965                         ( tr_torrentCountUncheckedPieces( tor ) /
966                           (double) tor->info.pieceCount )
967                       : 0.0;
968
969    s->activityDate = tor->activityDate;
970    s->addedDate    = tor->addedDate;
971    s->doneDate     = tor->doneDate;
972    s->startDate    = tor->startDate;
973
974    s->corruptEver     = tor->corruptCur    + tor->corruptPrev;
975    s->downloadedEver  = tor->downloadedCur + tor->downloadedPrev;
976    s->uploadedEver    = tor->uploadedCur   + tor->uploadedPrev;
977    s->haveValid       = tr_cpHaveValid( &tor->completion );
978    s->haveUnchecked   = tr_cpHaveTotal( &tor->completion ) - s->haveValid;
979
980    if( usableSeeds > 0 )
981    {
982        s->desiredAvailable = s->leftUntilDone;
983    }
984    else if( !s->leftUntilDone || !s->peersConnected )
985    {
986        s->desiredAvailable = 0;
987    }
988    else
989    {
990        tr_piece_index_t i;
991        tr_bitfield *    peerPieces = tr_peerMgrGetAvailable( tor );
992        s->desiredAvailable = 0;
993        for( i = 0; i < tor->info.pieceCount; ++i )
994            if( !tor->info.pieces[i].dnd && tr_bitfieldHasFast( peerPieces, i ) )
995                s->desiredAvailable += tr_cpMissingBlocksInPiece( &tor->completion, i );
996        s->desiredAvailable *= tor->blockSize;
997        tr_bitfieldFree( peerPieces );
998    }
999
1000    downloadedForRatio = s->downloadedEver ? s->downloadedEver : s->haveValid;
1001    s->ratio = tr_getRatio( s->uploadedEver, downloadedForRatio );
1002
1003    checkSeedRatio = tr_torrentGetSeedRatio( tor, &seedRatio );
1004
1005    switch( s->activity )
1006    {
1007        /* etaXLSpeed exists because if we use the piece speed directly,
1008         * brief fluctuations cause the ETA to jump all over the place.
1009         * so, etaXLSpeed is a smoothed-out version of the piece speed
1010         * to dampen the effect of fluctuations */
1011
1012        case TR_STATUS_DOWNLOAD:
1013            if( ( tor->etaDLSpeedCalculatedAt + 800 ) < now ) {
1014                tor->etaDLSpeed = ( ( tor->etaDLSpeedCalculatedAt + 4000 ) < now )
1015                    ? s->pieceDownloadSpeed /* if no recent previous speed, no need to smooth */
1016                    : 0.8*tor->etaDLSpeed + 0.2*s->pieceDownloadSpeed; /* smooth across 5 readings */
1017                tor->etaDLSpeedCalculatedAt = now;
1018            }
1019
1020            if( s->leftUntilDone > s->desiredAvailable )
1021                s->eta = TR_ETA_NOT_AVAIL;
1022            else if( s->pieceDownloadSpeed < 0.1 )
1023                s->eta = TR_ETA_UNKNOWN;
1024            else
1025                s->eta = s->leftUntilDone / tor->etaDLSpeed / 1024.0;
1026            break;
1027
1028        case TR_STATUS_SEED:
1029            if( checkSeedRatio )
1030            {
1031                if( ( tor->etaULSpeedCalculatedAt + 800 ) < now ) {
1032                    tor->etaULSpeed = ( ( tor->etaULSpeedCalculatedAt + 4000 ) < now )
1033                        ? s->pieceUploadSpeed /* if no recent previous speed, no need to smooth */
1034                        : 0.8*tor->etaULSpeed + 0.2*s->pieceUploadSpeed; /* smooth across 5 readings */
1035                    tor->etaULSpeedCalculatedAt = now;
1036                }
1037
1038                if( s->pieceUploadSpeed < 0.1 )
1039                    s->eta = TR_ETA_UNKNOWN;
1040                else
1041                    s->eta = (downloadedForRatio * (seedRatio - s->ratio)) / tor->etaULSpeed / 1024.0;
1042            }
1043            else
1044                s->eta = TR_ETA_NOT_AVAIL;
1045            break;
1046
1047        default:
1048            s->eta = TR_ETA_NOT_AVAIL;
1049            break;
1050    }
1051
1052    if( !checkSeedRatio || s->ratio >= seedRatio || s->ratio == TR_RATIO_INF )
1053        s->percentRatio = 1.0;
1054    else if( s->ratio == TR_RATIO_NA )
1055        s->percentRatio = 0.0;
1056    else
1057        s->percentRatio = s->ratio / seedRatio;
1058
1059    tr_torrentUnlock( tor );
1060
1061    return s;
1062}
1063
1064/***
1065****
1066***/
1067
1068static uint64_t
1069fileBytesCompleted( const tr_torrent * tor, tr_file_index_t index )
1070{
1071    uint64_t total = 0;
1072    const tr_file * f = &tor->info.files[index];
1073
1074    if( f->length )
1075    {
1076        const tr_block_index_t firstBlock = f->offset / tor->blockSize;
1077        const uint64_t lastByte = f->offset + f->length - 1;
1078        const tr_block_index_t lastBlock = lastByte / tor->blockSize;
1079
1080        if( firstBlock == lastBlock )
1081        {
1082            if( tr_cpBlockIsCompleteFast( &tor->completion, firstBlock ) )
1083                total = f->length;
1084        }
1085        else
1086        {
1087            tr_block_index_t i;
1088
1089            /* the first block */
1090            if( tr_cpBlockIsCompleteFast( &tor->completion, firstBlock ) )
1091                total += tor->blockSize - ( f->offset % tor->blockSize );
1092
1093            /* the middle blocks */
1094            if( f->firstPiece == f->lastPiece )
1095            {
1096                for( i=firstBlock+1; i<lastBlock; ++i )
1097                    if( tr_cpBlockIsCompleteFast( &tor->completion, i ) )
1098                        total += tor->blockSize;
1099            }
1100            else
1101            {
1102                uint64_t b = 0;
1103                const tr_block_index_t firstBlockOfLastPiece
1104                           = tr_torPieceFirstBlock( tor, f->lastPiece );
1105                const tr_block_index_t lastBlockOfFirstPiece
1106                           = tr_torPieceFirstBlock( tor, f->firstPiece )
1107                             + tr_torPieceCountBlocks( tor, f->firstPiece ) - 1;
1108
1109                /* the rest of the first piece */
1110                for( i=firstBlock+1; i<lastBlock && i<=lastBlockOfFirstPiece; ++i )
1111                    if( tr_cpBlockIsCompleteFast( &tor->completion, i ) )
1112                        ++b;
1113
1114                /* the middle pieces */
1115                if( f->firstPiece + 1 < f->lastPiece )
1116                    for( i=f->firstPiece+1; i<f->lastPiece; ++i )
1117                        b += tor->blockCountInPiece - tr_cpMissingBlocksInPiece( &tor->completion, i );
1118
1119                /* the rest of the last piece */
1120                for( i=firstBlockOfLastPiece; i<lastBlock; ++i )
1121                    if( tr_cpBlockIsCompleteFast( &tor->completion, i ) )
1122                        ++b;
1123
1124                b *= tor->blockSize;
1125                total += b;
1126            }
1127
1128            /* the last block */
1129            if( tr_cpBlockIsCompleteFast( &tor->completion, lastBlock ) )
1130                total += ( f->offset + f->length ) - ( tor->blockSize * lastBlock );
1131        }
1132    }
1133
1134    return total;
1135}
1136
1137tr_file_stat *
1138tr_torrentFiles( const tr_torrent * tor,
1139                 tr_file_index_t *  fileCount )
1140{
1141    tr_file_index_t       i;
1142    const tr_file_index_t n = tor->info.fileCount;
1143    tr_file_stat *        files = tr_new0( tr_file_stat, n );
1144    tr_file_stat *        walk = files;
1145    const tr_bool         isSeed = tor->completeness == TR_SEED;
1146
1147    assert( tr_isTorrent( tor ) );
1148
1149    for( i=0; i<n; ++i, ++walk ) {
1150        const uint64_t b = isSeed ? tor->info.files[i].length : fileBytesCompleted( tor, i );
1151        walk->bytesCompleted = b;
1152        walk->progress = tor->info.files[i].length > 0 ? ( (float)b / tor->info.files[i].length ) : 1.0;
1153    }
1154
1155    if( fileCount )
1156        *fileCount = n;
1157
1158    return files;
1159}
1160
1161void
1162tr_torrentFilesFree( tr_file_stat *            files,
1163                     tr_file_index_t fileCount UNUSED )
1164{
1165    tr_free( files );
1166}
1167
1168/***
1169****
1170***/
1171
1172float*
1173tr_torrentWebSpeeds( const tr_torrent * tor )
1174{
1175    return tr_isTorrent( tor )
1176         ? tr_peerMgrWebSpeeds( tor )
1177         : NULL;
1178}
1179
1180tr_peer_stat *
1181tr_torrentPeers( const tr_torrent * tor,
1182                 int *              peerCount )
1183{
1184    tr_peer_stat * ret = NULL;
1185
1186    if( tr_isTorrent( tor ) )
1187        ret = tr_peerMgrPeerStats( tor, peerCount );
1188
1189    return ret;
1190}
1191
1192void
1193tr_torrentPeersFree( tr_peer_stat * peers,
1194                     int peerCount  UNUSED )
1195{
1196    tr_free( peers );
1197}
1198
1199tr_tracker_stat *
1200tr_torrentTrackers( const tr_torrent * torrent,
1201                    int              * setmeTrackerCount )
1202{
1203    assert( tr_isTorrent( torrent ) );
1204
1205    return tr_announcerStats( torrent, setmeTrackerCount );
1206}
1207
1208void
1209tr_torrentTrackersFree( tr_tracker_stat * trackers,
1210                        int trackerCount )
1211{
1212    tr_announcerStatsFree( trackers, trackerCount );
1213}
1214
1215void
1216tr_torrentAvailability( const tr_torrent * tor,
1217                        int8_t *           tab,
1218                        int                size )
1219{
1220    tr_peerMgrTorrentAvailability( tor, tab, size );
1221}
1222
1223void
1224tr_torrentAmountFinished( const tr_torrent * tor,
1225                          float *            tab,
1226                          int                size )
1227{
1228    assert( tr_isTorrent( tor ) );
1229
1230    tr_torrentLock( tor );
1231    tr_cpGetAmountDone( &tor->completion, tab, size );
1232    tr_torrentUnlock( tor );
1233}
1234
1235static void
1236tr_torrentResetTransferStats( tr_torrent * tor )
1237{
1238    assert( tr_isTorrent( tor ) );
1239
1240    tr_torrentLock( tor );
1241
1242    tor->downloadedPrev += tor->downloadedCur;
1243    tor->downloadedCur   = 0;
1244    tor->uploadedPrev   += tor->uploadedCur;
1245    tor->uploadedCur     = 0;
1246    tor->corruptPrev    += tor->corruptCur;
1247    tor->corruptCur      = 0;
1248
1249    tr_torrentSetDirty( tor );
1250
1251    tr_torrentUnlock( tor );
1252}
1253
1254void
1255tr_torrentSetHasPiece( tr_torrent *     tor,
1256                       tr_piece_index_t pieceIndex,
1257                       tr_bool          has )
1258{
1259    assert( tr_isTorrent( tor ) );
1260    assert( pieceIndex < tor->info.pieceCount );
1261
1262    if( has )
1263        tr_cpPieceAdd( &tor->completion, pieceIndex );
1264    else
1265        tr_cpPieceRem( &tor->completion, pieceIndex );
1266}
1267
1268/***
1269****
1270***/
1271
1272static void
1273freeTorrent( tr_torrent * tor )
1274{
1275    tr_torrent * t;
1276    tr_session *  session = tor->session;
1277    tr_info *    inf = &tor->info;
1278
1279    assert( tr_isTorrent( tor ) );
1280    assert( !tor->isRunning );
1281
1282    tr_sessionLock( session );
1283
1284    tr_peerMgrRemoveTorrent( tor );
1285
1286    tr_cpDestruct( &tor->completion );
1287
1288    tr_announcerUnsubscribe( tor->tiers, tor->tiersSubscription );
1289    tr_announcerRemoveTorrent( session->announcer, tor );
1290
1291    tr_bitfieldDestruct( &tor->checkedPieces );
1292
1293    tr_free( tor->downloadDir );
1294    tr_free( tor->incompleteDir );
1295    tr_free( tor->peer_id );
1296
1297    if( tor == session->torrentList )
1298        session->torrentList = tor->next;
1299    else for( t = session->torrentList; t != NULL; t = t->next ) {
1300        if( t->next == tor ) {
1301            t->next = tor->next;
1302            break;
1303        }
1304    }
1305
1306    assert( session->torrentCount >= 1 );
1307    session->torrentCount--;
1308
1309    tr_bandwidthFree( tor->bandwidth );
1310
1311    tr_metainfoFree( inf );
1312    tr_free( tor );
1313
1314    tr_sessionUnlock( session );
1315}
1316
1317/**
1318***  Start/Stop Callback
1319**/
1320
1321static void
1322checkAndStartImpl( void * vtor )
1323{
1324    tr_torrent * tor = vtor;
1325
1326    assert( tr_isTorrent( tor ) );
1327
1328    tr_sessionLock( tor->session );
1329
1330    /** If we had local data before, but it's disappeared,
1331        stop the torrent and log an error. */
1332    if( tor->preVerifyTotal && !tr_cpHaveTotal( &tor->completion ) )
1333    {
1334        tr_torrentSetLocalError( tor, _( "No data found!  Reconnect any disconnected drives, use \"Set Location\", or restart the torrent to re-download." ) );
1335        tr_torrentStop( tor );
1336    }
1337    else
1338    {
1339        const time_t now = tr_time( );
1340        tor->isRunning = TRUE;
1341        tor->needsSeedRatioCheck = TRUE;
1342        tor->error = TR_STAT_OK;
1343        tor->errorString[0] = '\0';
1344        tor->completeness = tr_cpGetStatus( &tor->completion );
1345        tor->startDate = tor->anyDate = now;
1346
1347        tr_torrentResetTransferStats( tor );
1348        tr_announcerTorrentStarted( tor );
1349        tor->dhtAnnounceAt = now + tr_cryptoWeakRandInt( 20 );
1350        tor->dhtAnnounce6At = now + tr_cryptoWeakRandInt( 20 );
1351        tr_peerMgrStartTorrent( tor );
1352    }
1353
1354    tr_sessionUnlock( tor->session );
1355}
1356
1357static void
1358checkAndStartCB( tr_torrent * tor )
1359{
1360    assert( tr_isTorrent( tor ) );
1361    assert( tr_isSession( tor->session ) );
1362
1363    tr_runInEventThread( tor->session, checkAndStartImpl, tor );
1364}
1365
1366static void
1367torrentStart( tr_torrent * tor )
1368{
1369    assert( tr_isTorrent( tor ) );
1370
1371    tr_sessionLock( tor->session );
1372
1373    if( !tor->isRunning )
1374    {
1375        tr_verifyRemove( tor );
1376
1377        /* corresponds to the peer_id sent as a tracker request parameter.
1378         * one tracker admin says: "When the same torrent is opened and
1379         * closed and opened again without quitting Transmission ...
1380         * change the peerid. It would help sometimes if a stopped event
1381         * was missed to ensure that we didn't think someone was cheating. */
1382        tr_free( tor->peer_id );
1383        tor->peer_id = tr_peerIdNew( );
1384
1385        tor->isRunning = 1;
1386        tr_torrentSetDirty( tor );
1387        tor->preVerifyTotal = tr_cpHaveTotal( &tor->completion );
1388        tr_verifyAdd( tor, checkAndStartCB );
1389    }
1390
1391    tr_sessionUnlock( tor->session );
1392}
1393
1394void
1395tr_torrentStart( tr_torrent * tor )
1396{
1397    if( tr_isTorrent( tor ) )
1398        torrentStart( tor );
1399}
1400
1401static void
1402torrentRecheckDoneImpl( void * vtor )
1403{
1404    tr_torrent * tor = vtor;
1405
1406    assert( tr_isTorrent( tor ) );
1407    tr_torrentRecheckCompleteness( tor );
1408
1409    if( tor->preVerifyTotal && !tr_cpHaveTotal( &tor->completion ) )
1410    {
1411        tr_torrentSetLocalError( tor, _( "Can't find local data.  Try \"Set Location\" to find it, or restart the torrent to re-download." ) );
1412        tr_torrentStop( tor );
1413    }
1414    else if( tor->startAfterVerify )
1415    {
1416        tor->startAfterVerify = FALSE;
1417
1418        tr_torrentStart( tor );
1419    }
1420}
1421
1422static void
1423torrentRecheckDoneCB( tr_torrent * tor )
1424{
1425    assert( tr_isTorrent( tor ) );
1426
1427    tr_runInEventThread( tor->session, torrentRecheckDoneImpl, tor );
1428}
1429
1430static void
1431verifyTorrent( void * vtor )
1432{
1433    tr_torrent * tor = vtor;
1434
1435    assert( tr_isTorrent( tor ) );
1436    tr_sessionLock( tor->session );
1437
1438    /* if the torrent's already being verified, stop it */
1439    tr_verifyRemove( tor );
1440
1441    /* if the torrent's running, stop it & set the restart-after-verify flag */
1442    if( tor->startAfterVerify || tor->isRunning ) {
1443        tr_torrentStop( tor );
1444        tor->startAfterVerify = TRUE;
1445    }
1446
1447    /* add the torrent to the recheck queue */
1448    tor->preVerifyTotal = tr_cpHaveTotal( &tor->completion );
1449    tr_torrentUncheck( tor );
1450    tr_verifyAdd( tor, torrentRecheckDoneCB );
1451
1452    tr_sessionUnlock( tor->session );
1453}
1454
1455void
1456tr_torrentVerify( tr_torrent * tor )
1457{
1458    if( tr_isTorrent( tor ) )
1459        tr_runInEventThread( tor->session, verifyTorrent, tor );
1460}
1461
1462void
1463tr_torrentSave( tr_torrent * tor )
1464{
1465    assert( tr_isTorrent( tor ) );
1466
1467    if( tor->isDirty )
1468    {
1469        tor->isDirty = FALSE;
1470        tr_torrentSaveResume( tor );
1471    }
1472}
1473
1474static void
1475stopTorrent( void * vtor )
1476{
1477    tr_torrent * tor = vtor;
1478
1479    assert( tr_isTorrent( tor ) );
1480
1481    tr_verifyRemove( tor );
1482    tr_peerMgrStopTorrent( tor );
1483    tr_announcerTorrentStopped( tor );
1484
1485    tr_fdTorrentClose( tor->session, tor->uniqueId );
1486
1487    if( !tor->isDeleting )
1488        tr_torrentSave( tor );
1489}
1490
1491void
1492tr_torrentStop( tr_torrent * tor )
1493{
1494    assert( tr_isTorrent( tor ) );
1495
1496    if( tr_isTorrent( tor ) )
1497    {
1498        tr_sessionLock( tor->session );
1499
1500        tor->isRunning = 0;
1501        tr_torrentSetDirty( tor );
1502        tr_runInEventThread( tor->session, stopTorrent, tor );
1503
1504        tr_sessionUnlock( tor->session );
1505    }
1506}
1507
1508static void
1509closeTorrent( void * vtor )
1510{
1511    tr_benc * d;
1512    tr_torrent * tor = vtor;
1513
1514    assert( tr_isTorrent( tor ) );
1515
1516    d = tr_bencListAddDict( &tor->session->removedTorrents, 2 );
1517    tr_bencDictAddInt( d, "id", tor->uniqueId );
1518    tr_bencDictAddInt( d, "date", tr_time( ) );
1519
1520    stopTorrent( tor );
1521
1522    if( tor->isDeleting )
1523    {
1524        tr_metainfoRemoveSaved( tor->session, &tor->info );
1525        tr_torrentRemoveResume( tor );
1526    }
1527
1528    tor->isRunning = 0;
1529    freeTorrent( tor );
1530}
1531
1532void
1533tr_torrentFree( tr_torrent * tor )
1534{
1535    if( tr_isTorrent( tor ) )
1536    {
1537        tr_session * session = tor->session;
1538        assert( tr_isSession( session ) );
1539        tr_sessionLock( session );
1540
1541        tr_torrentClearCompletenessCallback( tor );
1542        tr_runInEventThread( session, closeTorrent, tor );
1543
1544        tr_sessionUnlock( session );
1545    }
1546}
1547
1548void
1549tr_torrentRemove( tr_torrent * tor )
1550{
1551    assert( tr_isTorrent( tor ) );
1552
1553    tor->isDeleting = 1;
1554    tr_torrentFree( tor );
1555}
1556
1557/**
1558***  Completeness
1559**/
1560
1561static const char *
1562getCompletionString( int type )
1563{
1564    switch( type )
1565    {
1566        /* Translators: this is a minor point that's safe to skip over, but FYI:
1567           "Complete" and "Done" are specific, different terms in Transmission:
1568           "Complete" means we've downloaded every file in the torrent.
1569           "Done" means we're done downloading the files we wanted, but NOT all
1570           that exist */
1571        case TR_PARTIAL_SEED:
1572            return _( "Done" );
1573
1574        case TR_SEED:
1575            return _( "Complete" );
1576
1577        default:
1578            return _( "Incomplete" );
1579    }
1580}
1581
1582static void
1583fireCompletenessChange( tr_torrent       * tor,
1584                        tr_completeness    status )
1585{
1586    assert( tr_isTorrent( tor ) );
1587    assert( ( status == TR_LEECH )
1588         || ( status == TR_SEED )
1589         || ( status == TR_PARTIAL_SEED ) );
1590
1591    if( tor->completeness_func )
1592        tor->completeness_func( tor, status, tor->completeness_func_user_data );
1593}
1594
1595void
1596tr_torrentSetCompletenessCallback( tr_torrent                    * tor,
1597                                   tr_torrent_completeness_func    func,
1598                                   void                          * user_data )
1599{
1600    assert( tr_isTorrent( tor ) );
1601
1602    tor->completeness_func = func;
1603    tor->completeness_func_user_data = user_data;
1604}
1605
1606void
1607tr_torrentClearCompletenessCallback( tr_torrent * torrent )
1608{
1609    tr_torrentSetCompletenessCallback( torrent, NULL, NULL );
1610}
1611
1612void
1613tr_torrentSetRatioLimitHitCallback( tr_torrent                     * tor,
1614                                    tr_torrent_ratio_limit_hit_func  func,
1615                                    void                           * user_data )
1616{
1617    assert( tr_isTorrent( tor ) );
1618
1619    tor->ratio_limit_hit_func = func;
1620    tor->ratio_limit_hit_func_user_data = user_data;
1621}
1622
1623void
1624tr_torrentClearRatioLimitHitCallback( tr_torrent * torrent )
1625{
1626    tr_torrentSetRatioLimitHitCallback( torrent, NULL, NULL );
1627}
1628
1629void
1630tr_torrentRecheckCompleteness( tr_torrent * tor )
1631{
1632    tr_completeness completeness;
1633
1634    assert( tr_isTorrent( tor ) );
1635
1636    tr_torrentLock( tor );
1637
1638    completeness = tr_cpGetStatus( &tor->completion );
1639
1640    if( completeness != tor->completeness )
1641    {
1642        const int recentChange = tor->downloadedCur != 0;
1643
1644        if( recentChange )
1645        {
1646            tr_torinf( tor, _( "State changed from \"%1$s\" to \"%2$s\"" ),
1647                      getCompletionString( tor->completeness ),
1648                      getCompletionString( completeness ) );
1649        }
1650
1651        tor->completeness = completeness;
1652        tor->needsSeedRatioCheck = TRUE;
1653        tr_fdTorrentClose( tor->session, tor->uniqueId );
1654
1655        /* if the torrent is a seed now,
1656         * and the files used to be in the incompleteDir,
1657         * then move them to the destination directory */
1658        if( tr_torrentIsSeed( tor ) && ( tor->currentDir == tor->incompleteDir ) )
1659            tr_torrentSetLocation( tor, tor->downloadDir, TRUE, NULL, NULL );
1660
1661        fireCompletenessChange( tor, completeness );
1662
1663        if( recentChange && tr_torrentIsSeed( tor ) )
1664        {
1665            tr_announcerTorrentCompleted( tor );
1666
1667            tor->doneDate = tor->anyDate = tr_time( );
1668        }
1669
1670        tr_torrentSetDirty( tor );
1671    }
1672
1673    tr_torrentUnlock( tor );
1674}
1675
1676/***
1677****
1678***/
1679
1680static void
1681tr_torrentFireMetadataCompleted( tr_torrent * tor )
1682{
1683    assert( tr_isTorrent( tor ) );
1684
1685    if( tor->metadata_func )
1686        tor->metadata_func( tor, tor->metadata_func_user_data );
1687}
1688
1689void
1690tr_torrentSetMetadataCallback( tr_torrent                * tor,
1691                               tr_torrent_metadata_func    func,
1692                               void                      * user_data )
1693{
1694    assert( tr_isTorrent( tor ) );
1695
1696    tor->metadata_func = func;
1697    tor->metadata_func_user_data = user_data;
1698}
1699
1700
1701/**
1702***  File priorities
1703**/
1704
1705void
1706tr_torrentInitFilePriority( tr_torrent *    tor,
1707                            tr_file_index_t fileIndex,
1708                            tr_priority_t   priority )
1709{
1710    tr_piece_index_t i;
1711    tr_file *        file;
1712
1713    assert( tr_isTorrent( tor ) );
1714    assert( fileIndex < tor->info.fileCount );
1715    assert( tr_isPriority( priority ) );
1716
1717    file = &tor->info.files[fileIndex];
1718    file->priority = priority;
1719    for( i = file->firstPiece; i <= file->lastPiece; ++i )
1720        tor->info.pieces[i].priority = calculatePiecePriority( tor, i, fileIndex );
1721}
1722
1723void
1724tr_torrentSetFilePriorities( tr_torrent *      tor,
1725                             tr_file_index_t * files,
1726                             tr_file_index_t   fileCount,
1727                             tr_priority_t     priority )
1728{
1729    tr_file_index_t i;
1730    assert( tr_isTorrent( tor ) );
1731    tr_torrentLock( tor );
1732
1733    for( i = 0; i < fileCount; ++i )
1734        if( files[i] < tor->info.fileCount )
1735            tr_torrentInitFilePriority( tor, files[i], priority );
1736    tr_torrentSetDirty( tor );
1737    tr_peerMgrRebuildRequests( tor );
1738
1739    tr_torrentUnlock( tor );
1740}
1741
1742tr_priority_t*
1743tr_torrentGetFilePriorities( const tr_torrent * tor )
1744{
1745    tr_file_index_t i;
1746    tr_priority_t * p;
1747
1748    assert( tr_isTorrent( tor ) );
1749
1750    tr_torrentLock( tor );
1751    p = tr_new0( tr_priority_t, tor->info.fileCount );
1752    for( i = 0; i < tor->info.fileCount; ++i )
1753        p[i] = tor->info.files[i].priority;
1754    tr_torrentUnlock( tor );
1755
1756    return p;
1757}
1758
1759/**
1760***  File DND
1761**/
1762
1763static void
1764setFileDND( tr_torrent * tor, tr_file_index_t fileIndex, int doDownload )
1765{
1766    tr_file *        file;
1767    const int        dnd = !doDownload;
1768    tr_piece_index_t firstPiece, firstPieceDND;
1769    tr_piece_index_t lastPiece, lastPieceDND;
1770    tr_file_index_t  i;
1771
1772    assert( tr_isTorrent( tor ) );
1773
1774    file = &tor->info.files[fileIndex];
1775    file->dnd = dnd;
1776    firstPiece = file->firstPiece;
1777    lastPiece = file->lastPiece;
1778
1779    /* can't set the first piece to DND unless
1780       every file using that piece is DND */
1781    firstPieceDND = dnd;
1782    if( fileIndex > 0 )
1783    {
1784        for( i = fileIndex - 1; firstPieceDND; --i )
1785        {
1786            if( tor->info.files[i].lastPiece != firstPiece )
1787                break;
1788            firstPieceDND = tor->info.files[i].dnd;
1789            if( !i )
1790                break;
1791        }
1792    }
1793
1794    /* can't set the last piece to DND unless
1795       every file using that piece is DND */
1796    lastPieceDND = dnd;
1797    for( i = fileIndex + 1; lastPieceDND && i < tor->info.fileCount; ++i )
1798    {
1799        if( tor->info.files[i].firstPiece != lastPiece )
1800            break;
1801        lastPieceDND = tor->info.files[i].dnd;
1802    }
1803
1804    if( firstPiece == lastPiece )
1805    {
1806        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
1807    }
1808    else
1809    {
1810        tr_piece_index_t pp;
1811        tor->info.pieces[firstPiece].dnd = firstPieceDND;
1812        tor->info.pieces[lastPiece].dnd = lastPieceDND;
1813        for( pp = firstPiece + 1; pp < lastPiece; ++pp )
1814            tor->info.pieces[pp].dnd = dnd;
1815    }
1816}
1817
1818void
1819tr_torrentInitFileDLs( tr_torrent      * tor,
1820                       tr_file_index_t * files,
1821                       tr_file_index_t   fileCount,
1822                       tr_bool           doDownload )
1823{
1824    tr_file_index_t i;
1825
1826    assert( tr_isTorrent( tor ) );
1827
1828    tr_torrentLock( tor );
1829
1830    for( i=0; i<fileCount; ++i )
1831        if( files[i] < tor->info.fileCount )
1832            setFileDND( tor, files[i], doDownload );
1833
1834    tr_cpInvalidateDND( &tor->completion );
1835    tor->needsSeedRatioCheck = TRUE;
1836
1837    tr_torrentUnlock( tor );
1838}
1839
1840void
1841tr_torrentSetFileDLs( tr_torrent *      tor,
1842                      tr_file_index_t * files,
1843                      tr_file_index_t   fileCount,
1844                      tr_bool           doDownload )
1845{
1846    assert( tr_isTorrent( tor ) );
1847    tr_torrentLock( tor );
1848
1849    tr_torrentInitFileDLs( tor, files, fileCount, doDownload );
1850    tr_torrentSetDirty( tor );
1851    tr_peerMgrRebuildRequests( tor );
1852
1853    tr_torrentUnlock( tor );
1854}
1855
1856/***
1857****
1858***/
1859
1860tr_priority_t
1861tr_torrentGetPriority( const tr_torrent * tor )
1862{
1863    assert( tr_isTorrent( tor ) );
1864
1865    return tor->bandwidth->priority;
1866}
1867
1868void
1869tr_torrentSetPriority( tr_torrent * tor, tr_priority_t priority )
1870{
1871    assert( tr_isTorrent( tor ) );
1872    assert( tr_isPriority( priority ) );
1873
1874    if( tor->bandwidth->priority != priority )
1875    {
1876        tor->bandwidth->priority = priority;
1877
1878        tr_torrentSetDirty( tor );
1879    }
1880}
1881
1882/***
1883****
1884***/
1885
1886void
1887tr_torrentSetPeerLimit( tr_torrent * tor,
1888                        uint16_t     maxConnectedPeers )
1889{
1890    assert( tr_isTorrent( tor ) );
1891
1892    if ( tor->maxConnectedPeers != maxConnectedPeers )
1893    {
1894        tor->maxConnectedPeers = maxConnectedPeers;
1895
1896        tr_torrentSetDirty( tor );
1897    }
1898}
1899
1900uint16_t
1901tr_torrentGetPeerLimit( const tr_torrent * tor )
1902{
1903    assert( tr_isTorrent( tor ) );
1904
1905    return tor->maxConnectedPeers;
1906}
1907
1908/***
1909****
1910***/
1911
1912tr_block_index_t
1913_tr_block( const tr_torrent * tor,
1914           tr_piece_index_t   index,
1915           uint32_t           offset )
1916{
1917    tr_block_index_t ret;
1918
1919    assert( tr_isTorrent( tor ) );
1920
1921    ret = index;
1922    ret *= ( tor->info.pieceSize / tor->blockSize );
1923    ret += offset / tor->blockSize;
1924    return ret;
1925}
1926
1927tr_bool
1928tr_torrentReqIsValid( const tr_torrent * tor,
1929                      tr_piece_index_t   index,
1930                      uint32_t           offset,
1931                      uint32_t           length )
1932{
1933    int err = 0;
1934
1935    assert( tr_isTorrent( tor ) );
1936
1937    if( index >= tor->info.pieceCount )
1938        err = 1;
1939    else if( length < 1 )
1940        err = 2;
1941    else if( ( offset + length ) > tr_torPieceCountBytes( tor, index ) )
1942        err = 3;
1943    else if( length > MAX_BLOCK_SIZE )
1944        err = 4;
1945    else if( tr_pieceOffset( tor, index, offset, length ) > tor->info.totalSize )
1946        err = 5;
1947
1948    if( err ) tr_tordbg( tor, "index %lu offset %lu length %lu err %d\n",
1949                              (unsigned long)index,
1950                              (unsigned long)offset,
1951                              (unsigned long)length,
1952                              err );
1953
1954    return !err;
1955}
1956
1957uint64_t
1958tr_pieceOffset( const tr_torrent * tor,
1959                tr_piece_index_t   index,
1960                uint32_t           offset,
1961                uint32_t           length )
1962{
1963    uint64_t ret;
1964
1965    assert( tr_isTorrent( tor ) );
1966
1967    ret = tor->info.pieceSize;
1968    ret *= index;
1969    ret += offset;
1970    ret += length;
1971    return ret;
1972}
1973
1974/***
1975****
1976***/
1977
1978void
1979tr_torrentSetPieceChecked( tr_torrent        * tor,
1980                           tr_piece_index_t    piece,
1981                           tr_bool             isChecked )
1982{
1983    assert( tr_isTorrent( tor ) );
1984
1985    if( isChecked )
1986        tr_bitfieldAdd( &tor->checkedPieces, piece );
1987    else
1988        tr_bitfieldRem( &tor->checkedPieces, piece );
1989}
1990
1991void
1992tr_torrentSetFileChecked( tr_torrent *    tor,
1993                          tr_file_index_t fileIndex,
1994                          tr_bool         isChecked )
1995{
1996    const tr_file *        file = &tor->info.files[fileIndex];
1997    const tr_piece_index_t begin = file->firstPiece;
1998    const tr_piece_index_t end = file->lastPiece + 1;
1999
2000    assert( tr_isTorrent( tor ) );
2001
2002    if( isChecked )
2003        tr_bitfieldAddRange( &tor->checkedPieces, begin, end );
2004    else
2005        tr_bitfieldRemRange( &tor->checkedPieces, begin, end );
2006}
2007
2008tr_bool
2009tr_torrentIsFileChecked( const tr_torrent * tor,
2010                         tr_file_index_t    fileIndex )
2011{
2012    const tr_file *        file = &tor->info.files[fileIndex];
2013    const tr_piece_index_t begin = file->firstPiece;
2014    const tr_piece_index_t end = file->lastPiece + 1;
2015    tr_piece_index_t       i;
2016    tr_bool                isChecked = TRUE;
2017
2018    assert( tr_isTorrent( tor ) );
2019
2020    for( i = begin; isChecked && i < end; ++i )
2021        if( !tr_torrentIsPieceChecked( tor, i ) )
2022            isChecked = FALSE;
2023
2024    return isChecked;
2025}
2026
2027void
2028tr_torrentUncheck( tr_torrent * tor )
2029{
2030    assert( tr_isTorrent( tor ) );
2031
2032    tr_bitfieldRemRange( &tor->checkedPieces, 0, tor->info.pieceCount );
2033}
2034
2035int
2036tr_torrentCountUncheckedPieces( const tr_torrent * tor )
2037{
2038    assert( tr_isTorrent( tor ) );
2039
2040    return tor->info.pieceCount - tr_bitfieldCountTrueBits( &tor->checkedPieces );
2041}
2042
2043time_t*
2044tr_torrentGetMTimes( const tr_torrent * tor, size_t * setme_n )
2045{
2046    size_t       i;
2047    const size_t n = tor->info.fileCount;
2048    time_t *     m = tr_new0( time_t, n );
2049
2050    assert( tr_isTorrent( tor ) );
2051
2052    for( i = 0; i < n; ++i )
2053    {
2054        struct stat sb;
2055        char * path = tr_torrentFindFile( tor, i );
2056        if( ( path != NULL ) && !stat( path, &sb ) && S_ISREG( sb.st_mode ) )
2057        {
2058#ifdef SYS_DARWIN
2059            m[i] = sb.st_mtimespec.tv_sec;
2060#else
2061            m[i] = sb.st_mtime;
2062#endif
2063        }
2064        tr_free( path );
2065    }
2066
2067    *setme_n = n;
2068    return m;
2069}
2070
2071/***
2072****
2073***/
2074
2075tr_bool
2076tr_torrentSetAnnounceList( tr_torrent             * tor,
2077                           const tr_tracker_info  * trackers,
2078                           int                      trackerCount )
2079{
2080    int i;
2081    tr_benc metainfo;
2082    tr_bool ok = TRUE;
2083    tr_torrentLock( tor );
2084
2085    assert( tr_isTorrent( tor ) );
2086
2087    /* look for bad URLs */
2088    for( i=0; ok && i<trackerCount; ++i )
2089        if( !tr_urlIsValidTracker( trackers[i].announce ) )
2090            ok = FALSE;
2091
2092    /* save to the .torrent file */
2093    if( ok && !tr_bencLoadFile( &metainfo, TR_FMT_BENC, tor->info.torrent ) )
2094    {
2095        tr_bool hasInfo;
2096        tr_info tmpInfo;
2097
2098        /* remove the old fields */
2099        tr_bencDictRemove( &metainfo, "announce" );
2100        tr_bencDictRemove( &metainfo, "announce-list" );
2101
2102        /* add the new fields */
2103        if( trackerCount > 0 )
2104        {
2105            tr_bencDictAddStr( &metainfo, "announce", trackers[0].announce );
2106        }
2107        if( trackerCount > 1 )
2108        {
2109            int i;
2110            int prevTier = -1;
2111            tr_benc * tier = NULL;
2112            tr_benc * announceList = tr_bencDictAddList( &metainfo, "announce-list", 0 );
2113
2114            for( i=0; i<trackerCount; ++i ) {
2115                if( prevTier != trackers[i].tier ) {
2116                    prevTier = trackers[i].tier;
2117                    tier = tr_bencListAddList( announceList, 0 );
2118                }
2119                tr_bencListAddStr( tier, trackers[i].announce );
2120            }
2121        }
2122
2123        /* try to parse it back again, to make sure it's good */
2124        memset( &tmpInfo, 0, sizeof( tr_info ) );
2125        if( tr_metainfoParse( tor->session, &metainfo, &tmpInfo,
2126                              &hasInfo, &tor->infoDictOffset, &tor->infoDictLength ) )
2127        {
2128            /* it's good, so keep these new trackers and free the old ones */
2129
2130            tr_info swap;
2131            swap.trackers = tor->info.trackers;
2132            swap.trackerCount = tor->info.trackerCount;
2133            tor->info.trackers = tmpInfo.trackers;
2134            tor->info.trackerCount = tmpInfo.trackerCount;
2135            tmpInfo.trackers = swap.trackers;
2136            tmpInfo.trackerCount = swap.trackerCount;
2137
2138            tr_metainfoFree( &tmpInfo );
2139            tr_bencToFile( &metainfo, TR_FMT_BENC, tor->info.torrent );
2140        }
2141
2142        /* cleanup */
2143        tr_bencFree( &metainfo );
2144
2145        /* tell the announcer to reload this torrent's tracker list */
2146        tr_announcerResetTorrent( tor->session->announcer, tor );
2147    }
2148
2149    tr_torrentUnlock( tor );
2150    return ok;
2151}
2152
2153/**
2154***
2155**/
2156
2157void
2158tr_torrentSetAddedDate( tr_torrent * tor,
2159                        time_t       t )
2160{
2161    assert( tr_isTorrent( tor ) );
2162
2163    tor->addedDate = t;
2164    tor->anyDate = MAX( tor->anyDate, tor->addedDate );
2165}
2166
2167void
2168tr_torrentSetActivityDate( tr_torrent * tor, time_t t )
2169{
2170    assert( tr_isTorrent( tor ) );
2171
2172    tor->activityDate = t;
2173    tor->anyDate = MAX( tor->anyDate, tor->activityDate );
2174}
2175
2176void
2177tr_torrentSetDoneDate( tr_torrent * tor,
2178                       time_t       t )
2179{
2180    assert( tr_isTorrent( tor ) );
2181
2182    tor->doneDate = t;
2183    tor->anyDate = MAX( tor->anyDate, tor->doneDate );
2184}
2185
2186/**
2187***
2188**/
2189
2190uint64_t
2191tr_torrentGetBytesLeftToAllocate( const tr_torrent * tor )
2192{
2193    tr_file_index_t i;
2194    uint64_t bytesLeft = 0;
2195
2196    assert( tr_isTorrent( tor ) );
2197
2198    for( i=0; i<tor->info.fileCount; ++i )
2199    {
2200        if( !tor->info.files[i].dnd )
2201        {
2202            struct stat sb;
2203            const uint64_t length = tor->info.files[i].length;
2204            char * path = tr_torrentFindFile( tor, i );
2205
2206            bytesLeft += length;
2207
2208            if( ( path != NULL ) && !stat( path, &sb )
2209                                 && S_ISREG( sb.st_mode )
2210                                 && ( (uint64_t)sb.st_size <= length ) )
2211                bytesLeft -= sb.st_size;
2212
2213            tr_free( path );
2214        }
2215    }
2216
2217    return bytesLeft;
2218}
2219
2220/****
2221*****  Removing the torrent's local data
2222****/
2223
2224static int
2225vstrcmp( const void * a, const void * b )
2226{
2227    return strcmp( a, b );
2228}
2229
2230static int
2231compareLongestFirst( const void * a, const void * b )
2232{
2233    const size_t alen = strlen( a );
2234    const size_t blen = strlen( b );
2235
2236    if( alen != blen )
2237        return alen > blen ? -1 : 1;
2238
2239    return vstrcmp( a, b );
2240}
2241
2242static void
2243addDirtyFile( const char  * root,
2244              const char  * filename,
2245              tr_ptrArray * dirtyFolders )
2246{
2247    char * dir = tr_dirname( filename );
2248
2249    /* add the parent folders to dirtyFolders until we reach the root or a known-dirty */
2250    while (     ( dir != NULL )
2251             && ( strlen( root ) <= strlen( dir ) )
2252             && ( tr_ptrArrayFindSorted( dirtyFolders, dir, vstrcmp ) == NULL ) )
2253    {
2254        char * tmp;
2255        tr_ptrArrayInsertSorted( dirtyFolders, tr_strdup( dir ), vstrcmp );
2256
2257        tmp = tr_dirname( dir );
2258        tr_free( dir );
2259        dir = tmp;
2260    }
2261
2262    tr_free( dir );
2263}
2264
2265static void
2266walkLocalData( const tr_torrent * tor,
2267               const char       * root,
2268               const char       * dir,
2269               const char       * base,
2270               tr_ptrArray      * torrentFiles,
2271               tr_ptrArray      * folders,
2272               tr_ptrArray      * dirtyFolders )
2273{
2274    int i;
2275    struct stat sb;
2276    char * buf;
2277
2278    assert( tr_isTorrent( tor ) );
2279
2280    buf = tr_buildPath( dir, base, NULL );
2281    i = stat( buf, &sb );
2282    if( !i )
2283    {
2284        DIR * odir = NULL;
2285
2286        if( S_ISDIR( sb.st_mode ) && ( ( odir = opendir ( buf ) ) ) )
2287        {
2288            struct dirent *d;
2289            tr_ptrArrayInsertSorted( folders, tr_strdup( buf ), vstrcmp );
2290            for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
2291                if( d->d_name && d->d_name[0] != '.' ) /* skip dotfiles */
2292                    walkLocalData( tor, root, buf, d->d_name, torrentFiles, folders, dirtyFolders );
2293            closedir( odir );
2294        }
2295        else if( S_ISREG( sb.st_mode ) && ( sb.st_size > 0 ) )
2296        {
2297            const char * sub = buf + strlen( tor->currentDir ) + strlen( TR_PATH_DELIMITER_STR );
2298            const tr_bool isTorrentFile = tr_ptrArrayFindSorted( torrentFiles, sub, vstrcmp ) != NULL;
2299            if( !isTorrentFile )
2300                addDirtyFile( root, buf, dirtyFolders );
2301        }
2302    }
2303
2304    tr_free( buf );
2305}
2306
2307static void
2308deleteLocalFile( const char * filename, tr_fileFunc fileFunc )
2309{
2310    struct stat sb;
2311    if( !stat( filename, &sb ) ) /* if file exists... */
2312        fileFunc( filename );
2313}
2314
2315static void
2316deleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
2317{
2318    int i, n;
2319    char ** s;
2320    tr_file_index_t f;
2321    tr_ptrArray torrentFiles = TR_PTR_ARRAY_INIT;
2322    tr_ptrArray folders      = TR_PTR_ARRAY_INIT;
2323    tr_ptrArray dirtyFolders = TR_PTR_ARRAY_INIT; /* dirty == contains non-torrent files */
2324
2325    const char * firstFile = tor->info.files[0].name;
2326    const char * cpch = strchr( firstFile, TR_PATH_DELIMITER );
2327    char * tmp = cpch ? tr_strndup( firstFile, cpch - firstFile ) : NULL;
2328    char * root = tr_buildPath( tor->currentDir, tmp, NULL );
2329
2330    assert( tr_isTorrent( tor ) );
2331
2332    for( f=0; f<tor->info.fileCount; ++f ) {
2333        tr_ptrArrayInsertSorted( &torrentFiles, tr_strdup( tor->info.files[f].name ), vstrcmp );
2334        tr_ptrArrayInsertSorted( &torrentFiles, tr_torrentBuildPartial( tor, f ), vstrcmp );
2335    }
2336
2337    /* build the set of folders and dirtyFolders */
2338    walkLocalData( tor, root, root, NULL, &torrentFiles, &folders, &dirtyFolders );
2339
2340    /* try to remove entire folders first, so that the recycle bin will be tidy */
2341    s = (char**) tr_ptrArrayPeek( &folders, &n );
2342    for( i=0; i<n; ++i )
2343        if( tr_ptrArrayFindSorted( &dirtyFolders, s[i], vstrcmp ) == NULL )
2344            deleteLocalFile( s[i], fileFunc );
2345
2346    /* now blow away any remaining torrent files, such as torrent files in dirty folders */
2347    for( f=0; f<tor->info.fileCount; ++f ) {
2348        char * path = tr_buildPath( tor->currentDir, tor->info.files[f].name, NULL );
2349        deleteLocalFile( path, fileFunc );
2350        tr_free( path );
2351    }
2352
2353    /* Now clean out the directories left empty from the previous step.
2354     * Work from deepest to shallowest s.t. lower folders
2355     * won't prevent the upper folders from being deleted */
2356    {
2357        tr_ptrArray cleanFolders = TR_PTR_ARRAY_INIT;
2358        s = (char**) tr_ptrArrayPeek( &folders, &n );
2359        for( i=0; i<n; ++i )
2360            if( tr_ptrArrayFindSorted( &dirtyFolders, s[i], vstrcmp ) == NULL )
2361                tr_ptrArrayInsertSorted( &cleanFolders, s[i], compareLongestFirst );
2362        s = (char**) tr_ptrArrayPeek( &cleanFolders, &n );
2363        for( i=0; i<n; ++i ) {
2364#ifdef SYS_DARWIN
2365            char * dsStore = tr_buildPath( s[i], ".DS_Store", NULL );
2366            deleteLocalFile( dsStore, fileFunc );
2367            tr_free( dsStore );
2368#endif
2369            deleteLocalFile( s[i], fileFunc );
2370        }
2371        tr_ptrArrayDestruct( &cleanFolders, NULL );
2372    }
2373
2374    /* cleanup */
2375    tr_ptrArrayDestruct( &dirtyFolders, tr_free );
2376    tr_ptrArrayDestruct( &folders, tr_free );
2377    tr_ptrArrayDestruct( &torrentFiles, tr_free );
2378    tr_free( root );
2379    tr_free( tmp );
2380}
2381
2382void
2383tr_torrentDeleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
2384{
2385    assert( tr_isTorrent( tor ) );
2386
2387    if( fileFunc == NULL )
2388        fileFunc = remove;
2389
2390    /* close all the files because we're about to delete them */
2391    tr_fdTorrentClose( tor->session, tor->uniqueId );
2392
2393    if( tor->info.fileCount > 1 )
2394    {
2395        deleteLocalData( tor, fileFunc );
2396    }
2397    else if( tor->info.fileCount == 1 )
2398    {
2399        char * tmp;
2400
2401        /* torrent only has one file */
2402        char * path = tr_buildPath( tor->currentDir, tor->info.files[0].name, NULL );
2403        deleteLocalFile( path, fileFunc );
2404        tr_free( path );
2405
2406        tmp = tr_torrentBuildPartial( tor, 0 );
2407        path = tr_buildPath( tor->currentDir, tmp, NULL );
2408        deleteLocalFile( path, fileFunc );
2409        tr_free( path );
2410        tr_free( tmp );
2411    }
2412}
2413
2414/***
2415****
2416***/
2417
2418struct LocationData
2419{
2420    tr_bool move_from_old_location;
2421    int * setme_state;
2422    double * setme_progress;
2423    char * location;
2424    tr_torrent * tor;
2425};
2426
2427static void
2428setLocation( void * vdata )
2429{
2430    tr_bool err = FALSE;
2431    tr_bool verify_needed = FALSE;
2432    struct LocationData * data = vdata;
2433    tr_torrent * tor = data->tor;
2434    const tr_bool do_move = data->move_from_old_location;
2435    const char * location = data->location;
2436    double bytesHandled = 0;
2437
2438    assert( tr_isTorrent( tor ) );
2439
2440    if( strcmp( location, tor->currentDir ) )
2441    {
2442        tr_file_index_t i;
2443
2444        /* bad idea to move files while they're being verified... */
2445        tr_verifyRemove( tor );
2446
2447        /* if the torrent is running, stop it and set a flag to
2448         * restart after we're done */
2449        if( tor->isRunning )
2450        {
2451            tr_torrentStop( tor );
2452            tor->startAfterVerify = TRUE;
2453        }
2454
2455        /* try to move the files.
2456         * FIXME: there are still all kinds of nasty cases, like what
2457         * if the target directory runs out of space halfway through... */
2458        for( i=0; !err && i<tor->info.fileCount; ++i )
2459        {
2460            const tr_file * f = &tor->info.files[i];
2461            const char * oldbase;
2462            char * sub;
2463            if( tr_torrentFindFile2( tor, i, &oldbase, &sub ) )
2464            {
2465                char * oldpath = tr_buildPath( oldbase, sub, NULL );
2466                char * newpath = tr_buildPath( location, sub, NULL );
2467
2468                if( do_move )
2469                {
2470                    tr_bool renamed = FALSE;
2471                    errno = 0;
2472                    tr_torinf( tor, "moving \"%s\" to \"%s\"", oldpath, newpath );
2473                    if( tr_moveFile( oldpath, newpath, &renamed ) )
2474                    {
2475                        err = TRUE;
2476                        tr_torerr( tor, "error moving \"%s\" to \"%s\": %s",
2477                                        oldpath, newpath, tr_strerror( errno ) );
2478                    }
2479                    else if( !renamed )
2480                    {
2481                        verify_needed = TRUE;
2482                    }
2483                }
2484
2485                tr_free( newpath );
2486                tr_free( oldpath );
2487                tr_free( sub );
2488            }
2489
2490            if( data->setme_progress )
2491            {
2492                bytesHandled += f->length;
2493                *data->setme_progress = bytesHandled / tor->info.totalSize;
2494            }
2495        }
2496
2497        if( !err )
2498        {
2499            /* blow away the leftover subdirectories in the old location */
2500            if( do_move )
2501                tr_torrentDeleteLocalData( tor, remove );
2502
2503            /* set the new location and reverify */
2504            tr_torrentSetDownloadDir( tor, location );
2505            if( verify_needed )
2506                tr_torrentVerify( tor );
2507            else if( tor->startAfterVerify ) {
2508                tor->startAfterVerify = FALSE;
2509                tr_torrentStart( tor );
2510            }
2511        }
2512    }
2513
2514    if( data->setme_state )
2515        *data->setme_state = err ? TR_LOC_ERROR : TR_LOC_DONE;
2516
2517    /* cleanup */
2518    tr_free( data->location );
2519    tr_free( data );
2520}
2521
2522void
2523tr_torrentSetLocation( tr_torrent  * tor,
2524                       const char  * location,
2525                       tr_bool       move_from_old_location,
2526                       double      * setme_progress,
2527                       int         * setme_state )
2528{
2529    struct LocationData * data;
2530
2531    assert( tr_isTorrent( tor ) );
2532
2533    if( setme_state )
2534        *setme_state = TR_LOC_MOVING;
2535    if( setme_progress )
2536        *setme_progress = 0;
2537
2538    /* run this in the libtransmission thread */
2539    data = tr_new( struct LocationData, 1 );
2540    data->tor = tor;
2541    data->location = tr_strdup( location );
2542    data->move_from_old_location = move_from_old_location;
2543    data->setme_state = setme_state;
2544    data->setme_progress = setme_progress;
2545    tr_runInEventThread( tor->session, setLocation, data );
2546}
2547
2548/***
2549****
2550***/
2551
2552void
2553tr_torrentCheckSeedRatio( tr_torrent * tor )
2554{
2555    double seedRatio;
2556
2557    assert( tr_isTorrent( tor ) );
2558
2559    /* if we're seeding and we've reached our seed ratio limit, stop the torrent */
2560    if( tor->isRunning && tr_torrentIsSeed( tor ) && tr_torrentGetSeedRatio( tor, &seedRatio ) )
2561    {
2562        const uint64_t up = tor->uploadedCur + tor->uploadedPrev;
2563        uint64_t down = tor->downloadedCur + tor->downloadedPrev;
2564        double ratio;
2565
2566        /* maybe we're the initial seeder and never downloaded anything... */
2567        if( down == 0 )
2568            down = tr_cpHaveValid( &tor->completion );
2569
2570        ratio = tr_getRatio( up, down );
2571
2572        if( ratio >= seedRatio || ratio == TR_RATIO_INF )
2573        {
2574            tr_torrentStop( tor );
2575
2576            /* set to no ratio limit to allow easy restarting */
2577            tr_torrentSetRatioMode( tor, TR_RATIOLIMIT_UNLIMITED );
2578
2579            /* maybe notify the client */
2580            if( tor->ratio_limit_hit_func != NULL )
2581                tor->ratio_limit_hit_func( tor, tor->ratio_limit_hit_func_user_data );
2582        }
2583    }
2584}
2585
2586/***
2587****
2588***/
2589
2590void
2591tr_torrentFileCompleted( tr_torrent * tor, tr_file_index_t fileNum )
2592{
2593    char * sub;
2594    const char * base;
2595
2596    /* close the file so that we can reopen in read-only mode as needed */
2597    tr_fdFileClose( tor->session, tor, fileNum );
2598
2599    /* if the torrent's filename on disk isn't the same as the one in the metadata,
2600     * then it's been modified to denote that it was a partial file.
2601     * Now that it's complete, use the proper filename. */
2602    if( tr_torrentFindFile2( tor, fileNum, &base, &sub ) )
2603    {
2604        const tr_file * file = &tor->info.files[fileNum];
2605
2606        if( strcmp( sub, file->name ) )
2607        {
2608            char * oldpath = tr_buildPath( base, sub, NULL );
2609            char * newpath = tr_buildPath( base, file->name, NULL );
2610
2611            if( rename( oldpath, newpath ) )
2612                tr_torerr( tor, "Error moving \"%s\" to \"%s\": %s", oldpath, newpath, tr_strerror( errno ) );
2613
2614            tr_free( newpath );
2615            tr_free( oldpath );
2616        }
2617
2618        tr_free( sub );
2619    }
2620}
2621
2622/***
2623****
2624***/
2625
2626static tr_bool
2627fileExists( const char * filename )
2628{
2629    struct stat sb;
2630    const tr_bool ok = !stat( filename, &sb );
2631    return ok;
2632}
2633
2634tr_bool
2635tr_torrentFindFile2( const tr_torrent * tor, tr_file_index_t fileNum,
2636                     const char ** base, char ** subpath )
2637{
2638    char * part;
2639    const tr_file * file;
2640    const char * b = NULL;
2641    const char * s = NULL;
2642
2643    assert( tr_isTorrent( tor ) );
2644    assert( fileNum < tor->info.fileCount );
2645
2646    file = &tor->info.files[fileNum];
2647    part = tr_torrentBuildPartial( tor, fileNum );
2648
2649    if( b == NULL ) {
2650        char * filename = tr_buildPath( tor->downloadDir, file->name, NULL );
2651        if( fileExists( filename ) ) {
2652            b = tor->downloadDir;
2653            s = file->name;
2654        }
2655        tr_free( filename );
2656    }
2657
2658    if( ( b == NULL ) && ( tor->incompleteDir != NULL ) ) {
2659        char * filename = tr_buildPath( tor->incompleteDir, file->name, NULL );
2660        if( fileExists( filename ) ) {
2661            b = tor->incompleteDir;
2662            s = file->name;
2663        }
2664        tr_free( filename );
2665    }
2666
2667    if( ( b == NULL ) && ( tor->incompleteDir != NULL ) ) {
2668        char * filename = tr_buildPath( tor->incompleteDir, part, NULL );
2669        if( fileExists( filename ) ) {
2670            b = tor->incompleteDir;
2671            s = part;
2672        }
2673        tr_free( filename );
2674    }
2675
2676    if( b == NULL) {
2677        char * filename = tr_buildPath( tor->downloadDir, part, NULL );
2678        if( fileExists( filename ) ) {
2679            b = tor->downloadDir;
2680            s = part;
2681        }
2682        tr_free( filename );
2683    }
2684
2685    if( base != NULL )
2686        *base = b;
2687    if( subpath != NULL )
2688        *subpath = tr_strdup( s );
2689
2690    tr_free( part );
2691    return b != NULL;
2692}
2693
2694char*
2695tr_torrentFindFile( const tr_torrent * tor, tr_file_index_t fileNum )
2696{
2697    char * subpath;
2698    char * ret = NULL;
2699    const char * base;
2700
2701    if( tr_torrentFindFile2( tor, fileNum, &base, &subpath ) )
2702    {
2703        ret = tr_buildPath( base, subpath, NULL );
2704        tr_free( subpath );
2705    }
2706
2707    return ret;
2708}
2709
2710/* Decide whether we should be looking for files in downloadDir or incompleteDir. */
2711static void
2712refreshCurrentDir( tr_torrent * tor )
2713{
2714    const char * dir = NULL;
2715    char * sub;
2716
2717    if( tor->incompleteDir == NULL )
2718        dir = tor->downloadDir;
2719    else if( !tr_torrentHasMetadata( tor ) ) /* no files to find */
2720        dir = tor->incompleteDir;
2721    else if( !tr_torrentFindFile2( tor, 0, &dir, &sub ) )
2722        dir = tor->incompleteDir;
2723
2724    assert( dir != NULL );
2725    assert( ( dir == tor->downloadDir ) || ( dir == tor->incompleteDir ) );
2726    tor->currentDir = dir;
2727}
2728
2729char*
2730tr_torrentBuildPartial( const tr_torrent * tor, tr_file_index_t fileNum )
2731{
2732    return tr_strdup_printf( "%s.part", tor->info.files[fileNum].name );
2733}
Note: See TracBrowser for help on using the repository browser.