source: trunk/libtransmission/torrent.c @ 9450

Last change on this file since 9450 was 9450, checked in by charles, 13 years ago

(trunk libT) tear out another line of code. subtle, this isn't. this is like figuring out which neighbor is stealing your newspaper in the morning by secretly shooting a different neighbor each morning until the paper stops disappearing.

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