source: trunk/libtransmission/torrent.c @ 7069

Last change on this file since 7069 was 7069, checked in by charles, 10 years ago

more fucking around with the speed measurements.

  • Property svn:keywords set to Date Rev Author Id
File size: 45.2 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 7069 2008-11-08 02:49:04Z charles $
3 *
4 * Copyright (c) 2005-2008 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <sys/types.h> /* stat */
26#include <sys/stat.h> /* stat */
27#include <unistd.h> /* stat */
28
29#include <assert.h>
30#include <limits.h> /* INT_MAX */
31#include <string.h> /* memcmp */
32#include <stdlib.h> /* qsort */
33
34#include "transmission.h"
35#include "bencode.h"
36#include "completion.h"
37#include "crypto.h" /* for tr_sha1 */
38#include "resume.h"
39#include "fdlimit.h" /* tr_fdFileClose */
40#include "metainfo.h"
41#include "peer-mgr.h"
42#include "ratecontrol.h"
43#include "torrent.h"
44#include "tracker.h"
45#include "trevent.h"
46#include "utils.h"
47#include "verify.h"
48
49#define MAX_BLOCK_SIZE ( 1024 * 16 )
50
51/***
52****
53***/
54
55int
56tr_torrentId( const tr_torrent * tor )
57{
58    return tor->uniqueId;
59}
60
61tr_torrent*
62tr_torrentFindFromId( tr_handle * handle,
63                      int         id )
64{
65    tr_torrent * tor = NULL;
66
67    while( ( tor = tr_torrentNext( handle, tor ) ) )
68        if( tor->uniqueId == id )
69            return tor;
70
71    return NULL;
72}
73
74tr_torrent*
75tr_torrentFindFromHashString( tr_handle *  handle,
76                              const char * str )
77{
78    tr_torrent * tor = NULL;
79
80    while( ( tor = tr_torrentNext( handle, tor ) ) )
81        if( !strcmp( str, tor->info.hashString ) )
82            return tor;
83
84    return NULL;
85}
86
87int
88tr_torrentExists( const tr_handle * handle,
89                  const uint8_t *   torrentHash )
90{
91    return tr_torrentFindFromHash( (tr_handle*)handle,
92                                  torrentHash ) != NULL;
93}
94
95tr_torrent*
96tr_torrentFindFromHash( tr_handle *     handle,
97                        const uint8_t * torrentHash )
98{
99    tr_torrent * tor = NULL;
100
101    while( ( tor = tr_torrentNext( handle, tor ) ) )
102        if( *tor->info.hash == *torrentHash )
103            if( !memcmp( tor->info.hash, torrentHash, SHA_DIGEST_LENGTH ) )
104                return tor;
105
106    return NULL;
107}
108
109tr_torrent*
110tr_torrentFindFromObfuscatedHash( tr_handle *     handle,
111                                  const uint8_t * obfuscatedTorrentHash )
112{
113    tr_torrent * tor = NULL;
114
115    while( ( tor = tr_torrentNext( handle, tor ) ) )
116        if( !memcmp( tor->obfuscatedHash, obfuscatedTorrentHash,
117                     SHA_DIGEST_LENGTH ) )
118            return tor;
119
120    return NULL;
121}
122
123/***
124****  LOCKS
125***/
126
127void
128tr_torrentLock( const tr_torrent * tor )
129{
130    tr_globalLock( tor->session );
131}
132
133void
134tr_torrentUnlock( const tr_torrent * tor )
135{
136    tr_globalUnlock( tor->session );
137}
138
139/***
140****  PER-TORRENT UL / DL SPEEDS
141***/
142
143void
144tr_torrentSetSpeedMode( tr_torrent *  tor,
145                        tr_direction  direction,
146                        tr_speedlimit mode )
147{
148    tr_speedlimit * limit = direction == TR_UP ? &tor->uploadLimitMode
149                                               : &tor->downloadLimitMode;
150
151    *limit = mode;
152}
153
154tr_speedlimit
155tr_torrentGetSpeedMode( const tr_torrent * tor,
156                        tr_direction       direction )
157{
158    return direction == TR_UP ? tor->uploadLimitMode
159                              : tor->downloadLimitMode;
160}
161
162void
163tr_torrentSetSpeedLimit( tr_torrent * tor,
164                         tr_direction direction,
165                         int          single_KiB_sec )
166{
167    switch( direction )
168    {
169        case TR_UP:
170            tor->uploadLimit = single_KiB_sec; break;
171
172        case TR_DOWN:
173            tor->downloadLimit = single_KiB_sec; break;
174
175        default:
176            assert( 0 );
177    }
178}
179
180int
181tr_torrentGetSpeedLimit( const tr_torrent * tor,
182                         tr_direction       direction )
183{
184    switch( direction )
185    {
186        case TR_UP:
187            return tor->uploadLimit;
188
189        case TR_DOWN:
190            return tor->downloadLimit;
191
192        default:
193            assert( 0 );
194    }
195}
196
197int
198tr_torrentIsPieceTransferAllowed( const tr_torrent  * tor,
199                                  tr_direction        direction )
200{
201    int isEnabled = FALSE;
202
203    switch( tr_torrentGetSpeedMode( tor, direction ) )
204    {
205        case TR_SPEEDLIMIT_GLOBAL:
206            isEnabled = !tr_sessionIsSpeedLimitEnabled( tor->session, direction )
207                      || tr_sessionGetSpeedLimit( tor->session, direction ) > 0;
208            break;
209
210        case TR_SPEEDLIMIT_SINGLE:
211            isEnabled = tr_torrentGetSpeedLimit( tor, direction ) > 0;
212            break;
213
214        case TR_SPEEDLIMIT_UNLIMITED:
215            isEnabled = TRUE;
216            break;
217
218        default:
219            assert( 0 && "unhandled speed mode" );
220            break;
221    }
222
223    return isEnabled;
224}
225
226/***
227****
228***/
229
230static void
231onTrackerResponse( void * tracker UNUSED,
232                   void *         vevent,
233                   void *         user_data )
234{
235    tr_torrent *       tor = user_data;
236    tr_tracker_event * event = vevent;
237
238    switch( event->messageType )
239    {
240        case TR_TRACKER_PEERS:
241        {
242            size_t   i, n;
243            tr_pex * pex = tr_peerMgrCompactToPex( event->compact,
244                                                   event->compactLen,
245                                                   NULL, 0, &n );
246            if( event->allAreSeeds )
247                tr_tordbg( tor, "Got %d seeds from tracker", (int)n );
248            else
249                tr_torinf( tor, _( "Got %d peers from tracker" ), (int)n );
250
251            for( i = 0; i < n; ++i )
252            {
253                if( event->allAreSeeds )
254                    pex[i].flags |= ADDED_F_SEED_FLAG;
255                tr_peerMgrAddPex( tor->session->peerMgr, tor->info.hash,
256                                  TR_PEER_FROM_TRACKER, pex + i );
257            }
258
259            tr_free( pex );
260            break;
261        }
262
263        case TR_TRACKER_WARNING:
264            tr_torerr( tor, _( "Tracker warning: \"%s\"" ), event->text );
265            tor->error = -1;
266            tr_strlcpy( tor->errorString, event->text,
267                       sizeof( tor->errorString ) );
268            break;
269
270        case TR_TRACKER_ERROR:
271            tr_torerr( tor, _( "Tracker error: \"%s\"" ), event->text );
272            tor->error = -2;
273            tr_strlcpy( tor->errorString, event->text,
274                       sizeof( tor->errorString ) );
275            break;
276
277        case TR_TRACKER_ERROR_CLEAR:
278            tor->error = 0;
279            tor->errorString[0] = '\0';
280            break;
281    }
282}
283
284/***
285****
286****  TORRENT INSTANTIATION
287****
288***/
289
290static int
291getBytePiece( const tr_info * info,
292              uint64_t        byteOffset )
293{
294    assert( info );
295    assert( info->pieceSize != 0 );
296
297    return byteOffset / info->pieceSize;
298}
299
300static void
301initFilePieces( tr_info *       info,
302                tr_file_index_t fileIndex )
303{
304    tr_file * file;
305    uint64_t  firstByte, lastByte;
306
307    assert( info );
308    assert( fileIndex < info->fileCount );
309
310    file = &info->files[fileIndex];
311    firstByte = file->offset;
312    lastByte = firstByte + ( file->length ? file->length - 1 : 0 );
313    file->firstPiece = getBytePiece( info, firstByte );
314    file->lastPiece = getBytePiece( info, lastByte );
315}
316
317static int
318pieceHasFile( tr_piece_index_t piece,
319              const tr_file *  file )
320{
321    return ( file->firstPiece <= piece ) && ( piece <= file->lastPiece );
322}
323
324static tr_priority_t
325calculatePiecePriority( const tr_torrent * tor,
326                        tr_piece_index_t   piece,
327                        int                fileHint )
328{
329    tr_file_index_t i;
330    int             priority = TR_PRI_LOW;
331
332    /* find the first file that has data in this piece */
333    if( fileHint >= 0 )
334    {
335        i = fileHint;
336        while( i > 0 && pieceHasFile( piece, &tor->info.files[i - 1] ) )
337            --i;
338    }
339    else
340    {
341        for( i = 0; i < tor->info.fileCount; ++i )
342            if( pieceHasFile( piece, &tor->info.files[i] ) )
343                break;
344    }
345
346    /* the piece's priority is the max of the priorities
347     * of all the files in that piece */
348    for( ; i < tor->info.fileCount; ++i )
349    {
350        const tr_file * file = &tor->info.files[i];
351
352        if( !pieceHasFile( piece, file ) )
353            break;
354
355        priority = MAX( priority, file->priority );
356
357        /* when dealing with multimedia files, getting the first and
358           last pieces can sometimes allow you to preview it a bit
359           before it's fully downloaded... */
360        if( file->priority >= TR_PRI_NORMAL )
361            if( file->firstPiece == piece || file->lastPiece == piece )
362                priority = TR_PRI_HIGH;
363    }
364
365    return priority;
366}
367
368static void
369tr_torrentInitFilePieces( tr_torrent * tor )
370{
371    tr_file_index_t  ff;
372    tr_piece_index_t pp;
373    uint64_t         offset = 0;
374    tr_info *        inf = &tor->info;
375
376    assert( inf );
377
378    for( ff = 0; ff < inf->fileCount; ++ff )
379    {
380        inf->files[ff].offset = offset;
381        offset += inf->files[ff].length;
382        initFilePieces( inf, ff );
383    }
384
385    for( pp = 0; pp < inf->pieceCount; ++pp )
386        inf->pieces[pp].priority = calculatePiecePriority( tor, pp, -1 );
387}
388
389int
390tr_torrentPromoteTracker( tr_torrent * tor,
391                          int          pos )
392{
393    int i;
394    int tier;
395
396    assert( tor );
397    assert( ( 0 <= pos ) && ( pos < tor->info.trackerCount ) );
398
399    /* the tier of the tracker we're promoting */
400    tier = tor->info.trackers[pos].tier;
401
402    /* find the index of that tier's first tracker */
403    for( i = 0; i < tor->info.trackerCount; ++i )
404        if( tor->info.trackers[i].tier == tier )
405            break;
406
407    assert( i < tor->info.trackerCount );
408
409    /* promote the tracker at `pos' to the front of the tier */
410    if( i != pos )
411    {
412        const tr_tracker_info tmp = tor->info.trackers[i];
413        tor->info.trackers[i] = tor->info.trackers[pos];
414        tor->info.trackers[pos] = tmp;
415    }
416
417    /* return the new position of the tracker that started out at [pos] */
418    return i;
419}
420
421struct RandomTracker
422{
423    tr_tracker_info    tracker;
424    int                random_value;
425};
426
427/* the tiers will be sorted from lowest to highest,
428 * and trackers are randomized within the tiers */
429static int
430compareRandomTracker( const void * va,
431                      const void * vb )
432{
433    const struct RandomTracker * a = va;
434    const struct RandomTracker * b = vb;
435
436    if( a->tracker.tier != b->tracker.tier )
437        return a->tracker.tier - b->tracker.tier;
438
439    return a->random_value - b->random_value;
440}
441
442static void
443randomizeTiers( tr_info * info )
444{
445    int                    i;
446    const int              n = info->trackerCount;
447    struct RandomTracker * r = tr_new0( struct RandomTracker, n );
448
449    for( i = 0; i < n; ++i )
450    {
451        r[i].tracker = info->trackers[i];
452        r[i].random_value = tr_cryptoRandInt( INT_MAX );
453    }
454    qsort( r, n, sizeof( struct RandomTracker ), compareRandomTracker );
455    for( i = 0; i < n; ++i )
456        info->trackers[i] = r[i].tracker;
457    tr_free( r );
458}
459
460static void torrentStart( tr_torrent * tor,
461                          int          reloadProgress );
462
463/**
464 * Decide on a block size.  constraints:
465 * (1) most clients decline requests over 16 KiB
466 * (2) pieceSize must be a multiple of block size
467 */
468static uint32_t
469getBlockSize( uint32_t pieceSize )
470{
471    uint32_t b = pieceSize;
472
473    while( b > MAX_BLOCK_SIZE )
474        b /= 2u;
475
476    if( !b || ( pieceSize % b ) ) /* not cleanly divisible */
477        return 0;
478    return b;
479}
480
481static void
482torrentRealInit( tr_handle *     h,
483                 tr_torrent *    tor,
484                 const tr_ctor * ctor )
485{
486    int        doStart;
487    uint64_t   loaded;
488    uint64_t   t;
489    tr_info *  info = &tor->info;
490    static int nextUniqueId = 1;
491
492    tr_globalLock( h );
493
494    tor->session   = h;
495    tor->uniqueId = nextUniqueId++;
496
497    randomizeTiers( info );
498
499    tor->rawSpeed[TR_CLIENT_TO_PEER] = tr_rcInit( );
500    tor->rawSpeed[TR_PEER_TO_CLIENT] = tr_rcInit( );
501    tor->pieceSpeed[TR_CLIENT_TO_PEER] = tr_rcInit( );
502    tor->pieceSpeed[TR_PEER_TO_CLIENT] = tr_rcInit( );
503
504    tor->blockSize = getBlockSize( info->pieceSize );
505
506    tor->lastPieceSize = info->totalSize % info->pieceSize;
507
508    if( !tor->lastPieceSize )
509        tor->lastPieceSize = info->pieceSize;
510
511    tor->lastBlockSize = info->totalSize % tor->blockSize;
512
513    if( !tor->lastBlockSize )
514        tor->lastBlockSize = tor->blockSize;
515
516    tor->blockCount =
517        ( info->totalSize + tor->blockSize - 1 ) / tor->blockSize;
518
519    tor->blockCountInPiece =
520        info->pieceSize / tor->blockSize;
521
522    tor->blockCountInLastPiece =
523        ( tor->lastPieceSize + tor->blockSize - 1 ) / tor->blockSize;
524
525    /* check our work */
526    assert( ( info->pieceSize % tor->blockSize ) == 0 );
527    t = info->pieceCount - 1;
528    t *= info->pieceSize;
529    t += tor->lastPieceSize;
530    assert( t == info->totalSize );
531    t = tor->blockCount - 1;
532    t *= tor->blockSize;
533    t += tor->lastBlockSize;
534    assert( t == info->totalSize );
535    t = info->pieceCount - 1;
536    t *= tor->blockCountInPiece;
537    t += tor->blockCountInLastPiece;
538    assert( t == (uint64_t)tor->blockCount );
539
540    tor->completion = tr_cpInit( tor );
541
542    tr_torrentInitFilePieces( tor );
543
544    tor->uploadLimit = 0;
545    tor->downloadLimit = 0;
546    tor->swarmSpeed = tr_rcInit( );
547
548    tr_sha1( tor->obfuscatedHash, "req2", 4,
549             info->hash, SHA_DIGEST_LENGTH,
550             NULL );
551
552    tr_peerMgrAddTorrent( h->peerMgr, tor );
553
554    if( !h->isPortSet )
555        tr_sessionSetPeerPort( h, TR_DEFAULT_PORT );
556
557    assert( !tor->downloadedCur );
558    assert( !tor->uploadedCur );
559
560    tor->error   = 0;
561
562    tor->checkedPieces = tr_bitfieldNew( tor->info.pieceCount );
563    tr_torrentUncheck( tor );
564
565    tor->addedDate = time( NULL ); /* this is a default value to be
566                                      overwritten by the resume file */
567    loaded = tr_torrentLoadResume( tor, ~0, ctor );
568
569    doStart = tor->isRunning;
570    tor->isRunning = 0;
571
572    if( !( loaded & TR_FR_SPEEDLIMIT ) )
573    {
574        tr_torrentSetSpeedLimit( tor, TR_UP,
575                                tr_sessionGetSpeedLimit( tor->session, TR_UP ) );
576        tr_torrentSetSpeedLimit( tor, TR_DOWN,
577                                tr_sessionGetSpeedLimit( tor->session,
578                                                         TR_DOWN ) );
579    }
580
581    tor->completeness = tr_cpGetStatus( tor->completion );
582
583    tor->tracker = tr_trackerNew( tor );
584    tor->trackerSubscription =
585        tr_trackerSubscribe( tor->tracker, onTrackerResponse,
586                             tor );
587
588    {
589        tr_torrent * it = NULL;
590        tr_torrent * last = NULL;
591        while( ( it = tr_torrentNext( h, it ) ) )
592            last = it;
593
594        if( !last )
595            h->torrentList = tor;
596        else
597            last->next = tor;
598        ++h->torrentCount;
599    }
600
601    tr_globalUnlock( h );
602
603    /* maybe save our own copy of the metainfo */
604    if( tr_ctorGetSave( ctor ) )
605    {
606        const tr_benc * val;
607        if( !tr_ctorGetMetainfo( ctor, &val ) )
608        {
609            const char * filename = tor->info.torrent;
610            tr_bencSaveFile( filename, val );
611            tr_sessionSetTorrentFile( tor->session, tor->info.hashString,
612                                      filename );
613        }
614    }
615
616    tr_metainfoMigrate( h, &tor->info );
617
618    if( doStart )
619        torrentStart( tor, FALSE );
620}
621
622int
623tr_torrentParse( const tr_handle * handle,
624                 const tr_ctor *   ctor,
625                 tr_info *         setmeInfo )
626{
627    int             err = 0;
628    int             doFree;
629    tr_info         tmp;
630    const tr_benc * metainfo;
631
632    if( setmeInfo == NULL )
633        setmeInfo = &tmp;
634    memset( setmeInfo, 0, sizeof( tr_info ) );
635
636    if( !err && tr_ctorGetMetainfo( ctor, &metainfo ) )
637        return TR_EINVALID;
638
639    err = tr_metainfoParse( handle, setmeInfo, metainfo );
640    doFree = !err && ( setmeInfo == &tmp );
641
642    if( !err && !getBlockSize( setmeInfo->pieceSize ) )
643        err = TR_EINVALID;
644
645    if( !err && tr_torrentExists( handle, setmeInfo->hash ) )
646        err = TR_EDUPLICATE;
647
648    if( doFree )
649        tr_metainfoFree( setmeInfo );
650
651    return err;
652}
653
654tr_torrent *
655tr_torrentNew( tr_handle *     handle,
656               const tr_ctor * ctor,
657               int *           setmeError )
658{
659    int          err;
660    tr_info      tmpInfo;
661    tr_torrent * tor = NULL;
662
663    err = tr_torrentParse( handle, ctor, &tmpInfo );
664    if( !err )
665    {
666        tor = tr_new0( tr_torrent, 1 );
667        tor->info = tmpInfo;
668        torrentRealInit( handle, tor, ctor );
669    }
670    else if( setmeError )
671    {
672        *setmeError = err;
673    }
674
675    return tor;
676}
677
678/**
679***
680**/
681
682void
683tr_torrentSetDownloadDir( tr_torrent * tor,
684                          const char * path )
685{
686    if( !path || !tor->downloadDir || strcmp( path, tor->downloadDir ) )
687    {
688        tr_free( tor->downloadDir );
689        tor->downloadDir = tr_strdup( path );
690        tr_torrentSaveResume( tor );
691    }
692}
693
694const char*
695tr_torrentGetDownloadDir( const tr_torrent * tor )
696{
697    return tor->downloadDir;
698}
699
700void
701tr_torrentChangeMyPort( tr_torrent * tor )
702{
703    if( tor->tracker )
704        tr_trackerChangeMyPort( tor->tracker );
705}
706
707int
708tr_torrentIsPrivate( const tr_torrent * tor )
709{
710    return tor
711           && tor->info.isPrivate;
712}
713
714int
715tr_torrentAllowsPex( const tr_torrent * tor )
716{
717    return tor
718           && tor->session->isPexEnabled
719           && !tr_torrentIsPrivate( tor );
720}
721
722static void
723tr_torrentManualUpdateImpl( void * vtor )
724{
725    tr_torrent * tor = vtor;
726
727    if( tor->isRunning )
728        tr_trackerReannounce( tor->tracker );
729}
730
731void
732tr_torrentManualUpdate( tr_torrent * tor )
733{
734    tr_runInEventThread( tor->session, tr_torrentManualUpdateImpl, tor );
735}
736
737int
738tr_torrentCanManualUpdate( const tr_torrent * tor )
739{
740    return ( tor )
741           && ( tor->isRunning )
742           && ( tr_trackerCanManualAnnounce( tor->tracker ) );
743}
744
745const tr_info *
746tr_torrentInfo( const tr_torrent * tor )
747{
748    return tor ? &tor->info : NULL;
749}
750
751const tr_stat *
752tr_torrentStatCached( tr_torrent * tor )
753{
754    const time_t now = time( NULL );
755
756    return tor && ( now == tor->lastStatTime ) ? &tor->stats
757           : tr_torrentStat( tor );
758}
759
760tr_torrent_activity
761tr_torrentGetActivity( tr_torrent * tor )
762{
763    tr_torrentRecheckCompleteness( tor );
764
765    if( tor->verifyState == TR_VERIFY_NOW )
766        return TR_STATUS_CHECK;
767    if( tor->verifyState == TR_VERIFY_WAIT )
768        return TR_STATUS_CHECK_WAIT;
769    if( !tor->isRunning )
770        return TR_STATUS_STOPPED;
771    if( tor->completeness == TR_CP_INCOMPLETE )
772        return TR_STATUS_DOWNLOAD;
773
774    return TR_STATUS_SEED;
775}
776
777const tr_stat *
778tr_torrentStat( tr_torrent * tor )
779{
780    tr_stat *               s;
781    struct tr_tracker *     tc;
782    const tr_tracker_info * ti;
783    int                     usableSeeds = 0;
784
785    if( !tor )
786        return NULL;
787
788    tr_torrentLock( tor );
789
790    tor->lastStatTime = time( NULL );
791
792    s = &tor->stats;
793    s->id = tor->uniqueId;
794    s->activity = tr_torrentGetActivity( tor );
795    s->error  = tor->error;
796    memcpy( s->errorString, tor->errorString,
797           sizeof( s->errorString ) );
798
799    tc = tor->tracker;
800    ti = tr_trackerGetAddress( tor->tracker );
801    s->announceURL = ti ? ti->announce : NULL;
802    s->scrapeURL   = ti ? ti->scrape   : NULL;
803    tr_trackerStat( tc, s );
804    tr_trackerGetCounts( tc, &s->timesCompleted,
805                         &s->leechers,
806                         &s->seeders );
807    tr_peerMgrTorrentStats( tor->session->peerMgr,
808                            tor->info.hash,
809                            &s->peersKnown,
810                            &s->peersConnected,
811                            &usableSeeds,
812                            &s->webseedsSendingToUs,
813                            &s->peersSendingToUs,
814                            &s->peersGettingFromUs,
815                            s->peersFrom );
816
817    s->rawUploadSpeed     = tr_rcRate( tor->rawSpeed[TR_UP] );
818    s->rawDownloadSpeed   = tr_rcRate( tor->rawSpeed[TR_DOWN] );
819    s->pieceUploadSpeed   = tr_rcRate( tor->pieceSpeed[TR_UP] );
820    s->pieceDownloadSpeed = tr_rcRate( tor->pieceSpeed[TR_DOWN] );
821
822    usableSeeds += tor->info.webseedCount;
823
824    s->percentComplete = tr_cpPercentComplete ( tor->completion );
825
826    s->percentDone = tr_cpPercentDone( tor->completion );
827    s->leftUntilDone = tr_cpLeftUntilDone( tor->completion );
828    s->sizeWhenDone = tr_cpSizeWhenDone( tor->completion );
829
830    s->recheckProgress = s->activity == TR_STATUS_CHECK
831                       ? 1.0 -
832                         ( tr_torrentCountUncheckedPieces( tor ) /
833                           (double) tor->info.pieceCount )
834                       : 0.0;
835
836    s->swarmSpeed = tr_rcRate( tor->swarmSpeed );
837
838    s->activityDate = tor->activityDate;
839    s->addedDate    = tor->addedDate;
840    s->doneDate     = tor->doneDate;
841    s->startDate    = tor->startDate;
842
843    s->corruptEver     = tor->corruptCur    + tor->corruptPrev;
844    s->downloadedEver  = tor->downloadedCur + tor->downloadedPrev;
845    s->uploadedEver    = tor->uploadedCur   + tor->uploadedPrev;
846    s->haveValid       = tr_cpHaveValid( tor->completion );
847    s->haveUnchecked   = tr_cpHaveTotal( tor->completion ) - s->haveValid;
848
849
850    if( usableSeeds > 0 )
851    {
852        s->desiredAvailable = s->leftUntilDone;
853    }
854    else if( !s->leftUntilDone || !s->peersConnected )
855    {
856        s->desiredAvailable = 0;
857    }
858    else
859    {
860        tr_piece_index_t i;
861        tr_bitfield *    peerPieces = tr_peerMgrGetAvailable(
862            tor->session->peerMgr,
863            tor->info.
864            hash );
865        s->desiredAvailable = 0;
866        for( i = 0; i < tor->info.pieceCount; ++i )
867            if( !tor->info.pieces[i].dnd && tr_bitfieldHas( peerPieces, i ) )
868                s->desiredAvailable += tr_cpMissingBlocksInPiece(
869                    tor->completion, i );
870        s->desiredAvailable *= tor->blockSize;
871        tr_bitfieldFree( peerPieces );
872    }
873
874    if( s->leftUntilDone > s->desiredAvailable )
875        s->eta = TR_ETA_NOT_AVAIL;
876    else if( s->pieceDownloadSpeed < 0.1 )
877        s->eta = TR_ETA_UNKNOWN;
878    else
879        s->eta = s->leftUntilDone / s->pieceDownloadSpeed / 1024.0;
880
881    s->ratio = tr_getRatio(
882        s->uploadedEver,
883        s->downloadedEver ? s->downloadedEver : s->
884        haveValid );
885
886    tr_torrentUnlock( tor );
887
888    return s;
889}
890
891/***
892****
893***/
894
895static uint64_t
896fileBytesCompleted( const tr_torrent * tor,
897                    tr_file_index_t    fileIndex )
898{
899    const tr_file *        file     =  &tor->info.files[fileIndex];
900    const tr_block_index_t firstBlock       =  file->offset /
901                                              tor->blockSize;
902    const uint64_t         firstBlockOffset =  file->offset %
903                                              tor->blockSize;
904    const uint64_t         lastOffset       =
905        file->length ? ( file->length - 1 ) : 0;
906    const tr_block_index_t lastBlock        =
907        ( file->offset + lastOffset ) / tor->blockSize;
908    const uint64_t         lastBlockOffset  =
909        ( file->offset + lastOffset ) % tor->blockSize;
910    uint64_t               haveBytes = 0;
911
912    assert( tor );
913    assert( fileIndex < tor->info.fileCount );
914    assert( file->offset + file->length <= tor->info.totalSize );
915    assert( ( firstBlock < tor->blockCount )
916          || ( !file->length && file->offset == tor->info.totalSize ) );
917    assert( ( lastBlock < tor->blockCount )
918          || ( !file->length && file->offset == tor->info.totalSize ) );
919    assert( firstBlock <= lastBlock );
920    assert( tr_torBlockPiece( tor, firstBlock ) == file->firstPiece );
921    assert( tr_torBlockPiece( tor, lastBlock ) == file->lastPiece );
922
923    if( firstBlock == lastBlock )
924    {
925        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
926            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
927    }
928    else
929    {
930        tr_block_index_t i;
931
932        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
933            haveBytes += tor->blockSize - firstBlockOffset;
934
935        for( i = firstBlock + 1; i < lastBlock; ++i )
936            if( tr_cpBlockIsComplete( tor->completion, i ) )
937                haveBytes += tor->blockSize;
938
939        if( tr_cpBlockIsComplete( tor->completion, lastBlock ) )
940            haveBytes += lastBlockOffset + 1;
941    }
942
943    return haveBytes;
944}
945
946tr_file_stat *
947tr_torrentFiles( const tr_torrent * tor,
948                 tr_file_index_t *  fileCount )
949{
950    tr_file_index_t       i;
951    const tr_file_index_t n = tor->info.fileCount;
952    tr_file_stat *        files = tr_new0( tr_file_stat, n );
953    tr_file_stat *        walk = files;
954
955    for( i = 0; i < n; ++i, ++walk )
956    {
957        const uint64_t b = fileBytesCompleted( tor, i );
958        walk->bytesCompleted = b;
959        walk->progress = tr_getRatio( b, tor->info.files[i].length );
960    }
961
962    if( fileCount )
963        *fileCount = n;
964
965    return files;
966}
967
968void
969tr_torrentFilesFree( tr_file_stat *            files,
970                     tr_file_index_t fileCount UNUSED )
971{
972    tr_free( files );
973}
974
975/***
976****
977***/
978
979float*
980tr_torrentWebSpeeds( const tr_torrent * tor )
981{
982    return tor ? tr_peerMgrWebSpeeds( tor->session->peerMgr, tor->info.hash )
983           : NULL;
984}
985
986tr_peer_stat *
987tr_torrentPeers( const tr_torrent * tor,
988                 int *              peerCount )
989{
990    tr_peer_stat * ret = NULL;
991
992    if( tor )
993        ret = tr_peerMgrPeerStats( tor->session->peerMgr,
994                                   tor->info.hash, peerCount );
995
996    return ret;
997}
998
999void
1000tr_torrentPeersFree( tr_peer_stat * peers,
1001                     int peerCount  UNUSED )
1002{
1003    tr_free( peers );
1004}
1005
1006void
1007tr_torrentAvailability( const tr_torrent * tor,
1008                        int8_t *           tab,
1009                        int                size )
1010{
1011    tr_peerMgrTorrentAvailability( tor->session->peerMgr,
1012                                   tor->info.hash,
1013                                   tab, size );
1014}
1015
1016void
1017tr_torrentAmountFinished( const tr_torrent * tor,
1018                          float *            tab,
1019                          int                size )
1020{
1021    tr_torrentLock( tor );
1022    tr_cpGetAmountDone( tor->completion, tab, size );
1023    tr_torrentUnlock( tor );
1024}
1025
1026void
1027tr_torrentResetTransferStats( tr_torrent * tor )
1028{
1029    tr_torrentLock( tor );
1030
1031    tor->downloadedPrev += tor->downloadedCur;
1032    tor->downloadedCur   = 0;
1033    tor->uploadedPrev   += tor->uploadedCur;
1034    tor->uploadedCur     = 0;
1035    tor->corruptPrev    += tor->corruptCur;
1036    tor->corruptCur      = 0;
1037
1038    tr_torrentUnlock( tor );
1039}
1040
1041void
1042tr_torrentSetHasPiece( tr_torrent *     tor,
1043                       tr_piece_index_t pieceIndex,
1044                       int              has )
1045{
1046    tr_torrentLock( tor );
1047
1048    assert( tor );
1049    assert( pieceIndex < tor->info.pieceCount );
1050
1051    if( has )
1052        tr_cpPieceAdd( tor->completion, pieceIndex );
1053    else
1054        tr_cpPieceRem( tor->completion, pieceIndex );
1055
1056    tr_torrentUnlock( tor );
1057}
1058
1059/***
1060****
1061***/
1062
1063static void
1064freeTorrent( tr_torrent * tor )
1065{
1066    tr_torrent * t;
1067    tr_handle *  h = tor->session;
1068    tr_info *    inf = &tor->info;
1069
1070    assert( tor );
1071    assert( !tor->isRunning );
1072
1073    tr_globalLock( h );
1074
1075    tr_peerMgrRemoveTorrent( h->peerMgr, tor->info.hash );
1076
1077    tr_cpClose( tor->completion );
1078
1079    tr_rcClose( tor->swarmSpeed );
1080
1081    tr_trackerUnsubscribe( tor->tracker, tor->trackerSubscription );
1082    tr_trackerFree( tor->tracker );
1083    tor->tracker = NULL;
1084
1085    tr_bitfieldFree( tor->checkedPieces );
1086
1087    tr_free( tor->downloadDir );
1088
1089    if( tor == h->torrentList )
1090        h->torrentList = tor->next;
1091    else for( t = h->torrentList; t != NULL; t = t->next )
1092        {
1093            if( t->next == tor )
1094            {
1095                t->next = tor->next;
1096                break;
1097            }
1098        }
1099
1100    assert( h->torrentCount >= 1 );
1101    h->torrentCount--;
1102
1103    tr_rcClose( tor->pieceSpeed[TR_PEER_TO_CLIENT] );
1104    tr_rcClose( tor->pieceSpeed[TR_CLIENT_TO_PEER] );
1105    tr_rcClose( tor->rawSpeed[TR_PEER_TO_CLIENT] );
1106    tr_rcClose( tor->rawSpeed[TR_CLIENT_TO_PEER] );
1107
1108    tr_metainfoFree( inf );
1109    tr_free( tor );
1110
1111    tr_globalUnlock( h );
1112}
1113
1114/**
1115***  Start/Stop Callback
1116**/
1117
1118static void
1119checkAndStartImpl( void * vtor )
1120{
1121    tr_torrent * tor = vtor;
1122
1123    tr_globalLock( tor->session );
1124
1125    tor->isRunning  = 1;
1126    *tor->errorString = '\0';
1127    tr_torrentResetTransferStats( tor );
1128    tor->completeness = tr_cpGetStatus( tor->completion );
1129    tr_torrentSaveResume( tor );
1130    tor->startDate = time( NULL );
1131    tr_trackerStart( tor->tracker );
1132    tr_peerMgrStartTorrent( tor->session->peerMgr, tor->info.hash );
1133
1134    tr_globalUnlock( tor->session );
1135}
1136
1137static void
1138checkAndStartCB( tr_torrent * tor )
1139{
1140    tr_runInEventThread( tor->session, checkAndStartImpl, tor );
1141}
1142
1143static void
1144torrentStart( tr_torrent * tor,
1145              int          reloadProgress )
1146{
1147    tr_globalLock( tor->session );
1148
1149    if( !tor->isRunning )
1150    {
1151        const int isVerifying = tr_verifyInProgress( tor );
1152
1153        if( !isVerifying && reloadProgress )
1154            tr_torrentLoadResume( tor, TR_FR_PROGRESS, NULL );
1155
1156        tor->isRunning = 1;
1157
1158        if( !isVerifying )
1159            tr_verifyAdd( tor, checkAndStartCB );
1160    }
1161
1162    tr_globalUnlock( tor->session );
1163}
1164
1165void
1166tr_torrentStart( tr_torrent * tor )
1167{
1168    if( tor )
1169        torrentStart( tor, TRUE );
1170}
1171
1172static void
1173torrentRecheckDoneImpl( void * vtor )
1174{
1175    tr_torrentRecheckCompleteness( vtor );
1176}
1177
1178static void
1179torrentRecheckDoneCB( tr_torrent * tor )
1180{
1181    tr_runInEventThread( tor->session, torrentRecheckDoneImpl, tor );
1182}
1183
1184void
1185tr_torrentVerify( tr_torrent * tor )
1186{
1187    tr_verifyRemove( tor );
1188
1189    tr_globalLock( tor->session );
1190
1191    tr_torrentUncheck( tor );
1192    tr_verifyAdd( tor, torrentRecheckDoneCB );
1193
1194    tr_globalUnlock( tor->session );
1195}
1196
1197static void
1198stopTorrent( void * vtor )
1199{
1200    tr_file_index_t i;
1201
1202    tr_torrent *    tor = vtor;
1203
1204    tr_verifyRemove( tor );
1205    tr_peerMgrStopTorrent( tor->session->peerMgr, tor->info.hash );
1206    tr_trackerStop( tor->tracker );
1207
1208    for( i = 0; i < tor->info.fileCount; ++i )
1209    {
1210        const tr_file * file = &tor->info.files[i];
1211        char * path = tr_buildPath( tor->downloadDir, file->name, NULL );
1212        tr_fdFileClose( path );
1213        tr_free( path );
1214    }
1215}
1216
1217void
1218tr_torrentStop( tr_torrent * tor )
1219{
1220    if( tor )
1221    {
1222        tr_globalLock( tor->session );
1223
1224        tor->isRunning = 0;
1225        if( !tor->isDeleting )
1226            tr_torrentSaveResume( tor );
1227        tr_runInEventThread( tor->session, stopTorrent, tor );
1228
1229        tr_globalUnlock( tor->session );
1230    }
1231}
1232
1233static void
1234closeTorrent( void * vtor )
1235{
1236    tr_torrent * tor = vtor;
1237
1238    tr_torrentSaveResume( tor );
1239    tor->isRunning = 0;
1240    stopTorrent( tor );
1241    if( tor->isDeleting )
1242    {
1243        tr_metainfoRemoveSaved( tor->session, &tor->info );
1244        tr_torrentRemoveResume( tor );
1245    }
1246    freeTorrent( tor );
1247}
1248
1249void
1250tr_torrentFree( tr_torrent * tor )
1251{
1252    if( tor )
1253    {
1254        tr_handle * handle = tor->session;
1255        tr_globalLock( handle );
1256
1257        tr_torrentClearCompletenessCallback( tor );
1258        tr_runInEventThread( handle, closeTorrent, tor );
1259
1260        tr_globalUnlock( handle );
1261    }
1262}
1263
1264void
1265tr_torrentRemove( tr_torrent * tor )
1266{
1267    tor->isDeleting = 1;
1268    tr_torrentFree( tor );
1269}
1270
1271/**
1272***  Completeness
1273**/
1274
1275static const char *
1276getCompletionString( int type )
1277{
1278    switch( type )
1279    {
1280        /* Translators: this is a minor point that's safe to skip over, but FYI:
1281           "Complete" and "Done" are specific, different terms in Transmission:
1282           "Complete" means we've downloaded every file in the torrent.
1283           "Done" means we're done downloading the files we wanted, but NOT all
1284           that exist */
1285        case TR_CP_DONE:
1286            return _( "Done" );
1287
1288        case TR_CP_COMPLETE:
1289            return _( "Complete" );
1290
1291        default:
1292            return _( "Incomplete" );
1293    }
1294}
1295
1296static void
1297fireCompletenessChange( tr_torrent       * tor,
1298                        tr_completeness    status )
1299{
1300    assert( tor );
1301    assert( ( status == TR_CP_INCOMPLETE )
1302         || ( status == TR_CP_DONE )
1303         || ( status == TR_CP_COMPLETE ) );
1304
1305    if( tor->completeness_func )
1306        tor->completeness_func( tor, status, tor->completeness_func_user_data );
1307}
1308
1309void
1310tr_torrentSetCompletenessCallback( tr_torrent                    * tor,
1311                                   tr_torrent_completeness_func    func,
1312                                   void                          * user_data )
1313{
1314    assert( tor );
1315    tor->completeness_func = func;
1316    tor->completeness_func_user_data = user_data;
1317}
1318
1319void
1320tr_torrentClearCompletenessCallback( tr_torrent * torrent )
1321{
1322    tr_torrentSetCompletenessCallback( torrent, NULL, NULL );
1323}
1324
1325void
1326tr_torrentRecheckCompleteness( tr_torrent * tor )
1327{
1328    tr_completeness completeness;
1329
1330    tr_torrentLock( tor );
1331
1332    completeness = tr_cpGetStatus( tor->completion );
1333
1334    if( completeness != tor->completeness )
1335    {
1336        const int recentChange = tor->downloadedCur != 0;
1337
1338        if( recentChange )
1339        {
1340            tr_torinf( tor, _( "State changed from \"%1$s\" to \"%2$s\"" ),
1341                      getCompletionString( tor->completeness ),
1342                      getCompletionString( completeness ) );
1343        }
1344
1345        tor->completeness = completeness;
1346        fireCompletenessChange( tor, completeness );
1347
1348        if( recentChange && ( completeness == TR_CP_COMPLETE ) )
1349        {
1350            tr_trackerCompleted( tor->tracker );
1351
1352            tor->doneDate = time( NULL );
1353        }
1354
1355        tr_torrentSaveResume( tor );
1356    }
1357
1358    tr_torrentUnlock( tor );
1359}
1360
1361int
1362tr_torrentIsSeed( const tr_torrent * tor )
1363{
1364    return tor->completeness == TR_CP_COMPLETE || tor->completeness == TR_CP_DONE;
1365}
1366
1367/**
1368***  File priorities
1369**/
1370
1371void
1372tr_torrentInitFilePriority( tr_torrent *    tor,
1373                            tr_file_index_t fileIndex,
1374                            tr_priority_t   priority )
1375{
1376    tr_piece_index_t i;
1377    tr_file *        file;
1378
1379    assert( tor );
1380    assert( fileIndex < tor->info.fileCount );
1381    assert(
1382        priority == TR_PRI_LOW || priority == TR_PRI_NORMAL || priority ==
1383        TR_PRI_HIGH );
1384
1385    file = &tor->info.files[fileIndex];
1386    file->priority = priority;
1387    for( i = file->firstPiece; i <= file->lastPiece; ++i )
1388        tor->info.pieces[i].priority = calculatePiecePriority( tor, i,
1389                                                               fileIndex );
1390}
1391
1392void
1393tr_torrentSetFilePriorities( tr_torrent *      tor,
1394                             tr_file_index_t * files,
1395                             tr_file_index_t   fileCount,
1396                             tr_priority_t     priority )
1397{
1398    tr_file_index_t i;
1399
1400    tr_torrentLock( tor );
1401
1402    for( i = 0; i < fileCount; ++i )
1403        tr_torrentInitFilePriority( tor, files[i], priority );
1404
1405    tr_torrentSaveResume( tor );
1406    tr_torrentUnlock( tor );
1407}
1408
1409tr_priority_t
1410tr_torrentGetFilePriority( const tr_torrent * tor,
1411                           tr_file_index_t    file )
1412{
1413    tr_priority_t ret;
1414
1415    tr_torrentLock( tor );
1416    assert( tor );
1417    assert( file < tor->info.fileCount );
1418    ret = tor->info.files[file].priority;
1419    tr_torrentUnlock( tor );
1420
1421    return ret;
1422}
1423
1424tr_priority_t*
1425tr_torrentGetFilePriorities( const tr_torrent * tor )
1426{
1427    tr_file_index_t i;
1428    tr_priority_t * p;
1429
1430    tr_torrentLock( tor );
1431    p = tr_new0( tr_priority_t, tor->info.fileCount );
1432    for( i = 0; i < tor->info.fileCount; ++i )
1433        p[i] = tor->info.files[i].priority;
1434    tr_torrentUnlock( tor );
1435
1436    return p;
1437}
1438
1439/**
1440***  File DND
1441**/
1442
1443int
1444tr_torrentGetFileDL( const tr_torrent * tor,
1445                     tr_file_index_t    file )
1446{
1447    int doDownload;
1448
1449    tr_torrentLock( tor );
1450
1451    assert( file < tor->info.fileCount );
1452    doDownload = !tor->info.files[file].dnd;
1453
1454    tr_torrentUnlock( tor );
1455    return doDownload != 0;
1456}
1457
1458static void
1459setFileDND( tr_torrent *    tor,
1460            tr_file_index_t fileIndex,
1461            int             doDownload )
1462{
1463    tr_file *        file;
1464    const int        dnd = !doDownload;
1465    tr_piece_index_t firstPiece, firstPieceDND;
1466    tr_piece_index_t lastPiece, lastPieceDND;
1467    tr_file_index_t  i;
1468
1469    file = &tor->info.files[fileIndex];
1470    file->dnd = dnd;
1471    firstPiece = file->firstPiece;
1472    lastPiece = file->lastPiece;
1473
1474    /* can't set the first piece to DND unless
1475       every file using that piece is DND */
1476    firstPieceDND = dnd;
1477    if( fileIndex > 0 )
1478    {
1479        for( i = fileIndex - 1; firstPieceDND; --i )
1480        {
1481            if( tor->info.files[i].lastPiece != firstPiece )
1482                break;
1483            firstPieceDND = tor->info.files[i].dnd;
1484            if( !i )
1485                break;
1486        }
1487    }
1488
1489    /* can't set the last piece to DND unless
1490       every file using that piece is DND */
1491    lastPieceDND = dnd;
1492    for( i = fileIndex + 1; lastPieceDND && i < tor->info.fileCount; ++i )
1493    {
1494        if( tor->info.files[i].firstPiece != lastPiece )
1495            break;
1496        lastPieceDND = tor->info.files[i].dnd;
1497    }
1498
1499    if( firstPiece == lastPiece )
1500    {
1501        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
1502    }
1503    else
1504    {
1505        tr_piece_index_t pp;
1506        tor->info.pieces[firstPiece].dnd = firstPieceDND;
1507        tor->info.pieces[lastPiece].dnd = lastPieceDND;
1508        for( pp = firstPiece + 1; pp < lastPiece; ++pp )
1509            tor->info.pieces[pp].dnd = dnd;
1510    }
1511}
1512
1513void
1514tr_torrentInitFileDLs( tr_torrent *      tor,
1515                       tr_file_index_t * files,
1516                       tr_file_index_t   fileCount,
1517                       int               doDownload )
1518{
1519    tr_file_index_t i;
1520
1521    tr_torrentLock( tor );
1522
1523    for( i = 0; i < fileCount; ++i )
1524        setFileDND( tor, files[i], doDownload );
1525    tr_cpInvalidateDND ( tor->completion );
1526
1527    tr_torrentUnlock( tor );
1528}
1529
1530void
1531tr_torrentSetFileDLs( tr_torrent *      tor,
1532                      tr_file_index_t * files,
1533                      tr_file_index_t   fileCount,
1534                      int               doDownload )
1535{
1536    tr_torrentLock( tor );
1537    tr_torrentInitFileDLs( tor, files, fileCount, doDownload );
1538    tr_torrentSaveResume( tor );
1539    tr_torrentUnlock( tor );
1540}
1541
1542/***
1543****
1544***/
1545
1546void
1547tr_torrentSetPeerLimit( tr_torrent * tor,
1548                        uint16_t     maxConnectedPeers )
1549{
1550    tor->maxConnectedPeers = maxConnectedPeers;
1551}
1552
1553uint16_t
1554tr_torrentGetPeerLimit( const tr_torrent * tor )
1555{
1556    return tor->maxConnectedPeers;
1557}
1558
1559/***
1560****
1561***/
1562
1563tr_block_index_t
1564_tr_block( const tr_torrent * tor,
1565           tr_piece_index_t   index,
1566           uint32_t           offset )
1567{
1568    const tr_info *  inf = &tor->info;
1569    tr_block_index_t ret;
1570
1571    ret = index;
1572    ret *= ( inf->pieceSize / tor->blockSize );
1573    ret += offset / tor->blockSize;
1574    return ret;
1575}
1576
1577int
1578tr_torrentReqIsValid( const tr_torrent * tor,
1579                      tr_piece_index_t   index,
1580                      uint32_t           offset,
1581                      uint32_t           length )
1582{
1583    int err = 0;
1584
1585    if( index >= tor->info.pieceCount )
1586        err = 1;
1587    else if( length < 1 )
1588        err = 2;
1589    else if( ( offset + length ) > tr_torPieceCountBytes( tor, index ) )
1590        err = 3;
1591    else if( length > MAX_BLOCK_SIZE )
1592        err = 4;
1593    else if( tr_pieceOffset( tor, index, offset, length ) > tor->info.totalSize )
1594        err = 5;
1595
1596    if( err ) fprintf( stderr, "index %lu offset %lu length %lu err %d\n",
1597                       (unsigned long)index, (unsigned long)offset,
1598                       (unsigned long)length,
1599                       err );
1600
1601    return !err;
1602}
1603
1604uint64_t
1605tr_pieceOffset( const tr_torrent * tor,
1606                tr_piece_index_t   index,
1607                uint32_t           offset,
1608                uint32_t           length )
1609{
1610    uint64_t ret;
1611
1612    ret = tor->info.pieceSize;
1613    ret *= index;
1614    ret += offset;
1615    ret += length;
1616    return ret;
1617}
1618
1619/***
1620****
1621***/
1622
1623int
1624tr_torrentIsPieceChecked( const tr_torrent * tor,
1625                          tr_piece_index_t   piece )
1626{
1627    return tr_bitfieldHas( tor->checkedPieces, piece );
1628}
1629
1630void
1631tr_torrentSetPieceChecked( tr_torrent *     tor,
1632                           tr_piece_index_t piece,
1633                           int              isChecked )
1634{
1635    if( isChecked )
1636        tr_bitfieldAdd( tor->checkedPieces, piece );
1637    else
1638        tr_bitfieldRem( tor->checkedPieces, piece );
1639}
1640
1641void
1642tr_torrentSetFileChecked( tr_torrent *    tor,
1643                          tr_file_index_t fileIndex,
1644                          int             isChecked )
1645{
1646    const tr_file *        file = &tor->info.files[fileIndex];
1647    const tr_piece_index_t begin = file->firstPiece;
1648    const tr_piece_index_t end = file->lastPiece + 1;
1649
1650    if( isChecked )
1651        tr_bitfieldAddRange ( tor->checkedPieces, begin, end );
1652    else
1653        tr_bitfieldRemRange ( tor->checkedPieces, begin, end );
1654}
1655
1656int
1657tr_torrentIsFileChecked( const tr_torrent * tor,
1658                         tr_file_index_t    fileIndex )
1659{
1660    const tr_file *        file = &tor->info.files[fileIndex];
1661    const tr_piece_index_t begin = file->firstPiece;
1662    const tr_piece_index_t end = file->lastPiece + 1;
1663    tr_piece_index_t       i;
1664    int                    isChecked = TRUE;
1665
1666    for( i = begin; isChecked && i < end; ++i )
1667        if( !tr_torrentIsPieceChecked( tor, i ) )
1668            isChecked = FALSE;
1669
1670    return isChecked;
1671}
1672
1673void
1674tr_torrentUncheck( tr_torrent * tor )
1675{
1676    tr_bitfieldRemRange ( tor->checkedPieces, 0, tor->info.pieceCount );
1677}
1678
1679int
1680tr_torrentCountUncheckedPieces( const tr_torrent * tor )
1681{
1682    return tor->info.pieceCount - tr_bitfieldCountTrueBits(
1683               tor->checkedPieces );
1684}
1685
1686time_t*
1687tr_torrentGetMTimes( const tr_torrent * tor,
1688                     size_t *           setme_n )
1689{
1690    size_t       i;
1691    const size_t n = tor->info.fileCount;
1692    time_t *     m = tr_new0( time_t, n );
1693
1694    for( i = 0; i < n; ++i )
1695    {
1696        struct stat sb;
1697        char * path = tr_buildPath( tor->downloadDir, tor->info.files[i].name, NULL );
1698        if( !stat( path, &sb ) )
1699        {
1700#ifdef SYS_DARWIN
1701            m[i] = sb.st_mtimespec.tv_sec;
1702#else
1703            m[i] = sb.st_mtime;
1704#endif
1705        }
1706        tr_free( path );
1707    }
1708
1709    *setme_n = n;
1710    return m;
1711}
1712
1713/***
1714****
1715***/
1716
1717void
1718tr_torrentSetAnnounceList( tr_torrent *            tor,
1719                           const tr_tracker_info * trackers,
1720                           int                     trackerCount )
1721{
1722    tr_benc metainfo;
1723
1724    /* save to the .torrent file */
1725    if( !tr_bencLoadFile( tor->info.torrent, &metainfo ) )
1726    {
1727        int       i;
1728        int       prevTier = -1;
1729        tr_benc * tier = NULL;
1730        tr_benc * announceList;
1731        tr_info   tmpInfo;
1732
1733        /* remove the old fields */
1734        tr_bencDictRemove( &metainfo, "announce" );
1735        tr_bencDictRemove( &metainfo, "announce-list" );
1736
1737        /* add the new fields */
1738        tr_bencDictAddStr( &metainfo, "announce", trackers[0].announce );
1739        announceList = tr_bencDictAddList( &metainfo, "announce-list", 0 );
1740        for( i = 0; i < trackerCount; ++i )
1741        {
1742            if( prevTier != trackers[i].tier )
1743            {
1744                prevTier = trackers[i].tier;
1745                tier = tr_bencListAddList( announceList, 0 );
1746            }
1747            tr_bencListAddStr( tier, trackers[i].announce );
1748        }
1749
1750        /* try to parse it back again, to make sure it's good */
1751        memset( &tmpInfo, 0, sizeof( tr_info ) );
1752        if( !tr_metainfoParse( tor->session, &tmpInfo, &metainfo ) )
1753        {
1754            /* if it's good, save it and use it */
1755            tr_metainfoFree( &tor->info );
1756            tor->info = tmpInfo;
1757            tr_torrentInitFilePieces( tor );
1758            tr_bencSaveFile( tor->info.torrent, &metainfo );
1759        }
1760
1761        /* cleanup */
1762        tr_bencFree( &metainfo );
1763    }
1764}
1765
1766/**
1767***
1768**/
1769
1770/** @deprecated this method will be removed in 1.40 */
1771void
1772tr_torrentSetAddedDate( tr_torrent * tor,
1773                        time_t       t )
1774{
1775    tor->addedDate = t;
1776}
1777
1778/** @deprecated this method will be removed in 1.40 */
1779void
1780tr_torrentSetActivityDate( tr_torrent * tor,
1781                           time_t       t )
1782{
1783    tor->activityDate = t;
1784}
1785
1786/** @deprecated this method will be removed in 1.40 */
1787void
1788tr_torrentSetDoneDate( tr_torrent * tor,
1789                       time_t       t )
1790{
1791    tor->doneDate = t;
1792}
1793
1794/**
1795***
1796**/
1797
1798uint64_t
1799tr_torrentGetBytesLeftToAllocate( const tr_torrent * tor )
1800{
1801    const tr_file * it;
1802    const tr_file * end;
1803    struct stat sb;
1804    uint64_t bytesLeft = 0;
1805
1806    for( it=tor->info.files, end=it+tor->info.fileCount; it!=end; ++it )
1807    {
1808        if( !it->dnd )
1809        {
1810            char * path = tr_buildPath( tor->downloadDir, it->name, NULL );
1811
1812            bytesLeft += it->length;
1813
1814            if( !stat( path, &sb )
1815                    && S_ISREG( sb.st_mode )
1816                    && ( (uint64_t)sb.st_size <= it->length ) )
1817                bytesLeft -= sb.st_size;
1818
1819            tr_free( path );
1820        }
1821    }
1822
1823    return bytesLeft;
1824}
Note: See TracBrowser for help on using the repository browser.