source: trunk/libtransmission/torrent.c @ 6429

Last change on this file since 6429 was 6429, checked in by charles, 15 years ago

#1135: crashes when adding torrent, with "assert( ( info->pieceSize % tor->blockSize ) == 0 );"

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