source: trunk/libtransmission/torrent.c @ 7385

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

(trunk libT) more semantic cleanup: tr_handle->tr_session, int->tr_bool, int->tr_port

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