source: trunk/libtransmission/torrent.c @ 6425

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

minor text cleanup

  • Property svn:keywords set to Date Rev Author Id
File size: 40.9 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 6425 2008-08-01 16:43:22Z 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
392static void
393torrentRealInit( tr_handle     * h,
394                 tr_torrent    * tor,
395                 const tr_ctor * ctor )
396{
397    int doStart;
398    uint64_t loaded;
399    uint64_t t;
400    tr_info * info = &tor->info;
401    static int nextUniqueId = 1;
402   
403    tr_globalLock( h );
404
405    tor->handle   = h;
406    tor->uniqueId = nextUniqueId++;
407
408    randomizeTiers( info );
409
410    /**
411     * Decide on a block size.  constraints:
412     * (1) most clients decline requests over 16 KiB
413     * (2) pieceSize must be a multiple of block size
414     */
415    tor->blockSize = info->pieceSize;
416    while( tor->blockSize > MAX_BLOCK_SIZE )
417        tor->blockSize /= 2;
418
419    tor->lastPieceSize = info->totalSize % info->pieceSize;
420
421    if( !tor->lastPieceSize )
422         tor->lastPieceSize = info->pieceSize;
423
424    tor->lastBlockSize = info->totalSize % tor->blockSize;
425
426    if( !tor->lastBlockSize )
427         tor->lastBlockSize = tor->blockSize;
428
429    tor->blockCount =
430        ( info->totalSize + tor->blockSize - 1 ) / tor->blockSize;
431
432    tor->blockCountInPiece =
433        info->pieceSize / tor->blockSize;
434
435    tor->blockCountInLastPiece =
436        ( tor->lastPieceSize + tor->blockSize - 1 ) / tor->blockSize;
437
438    /* check our work */
439    assert( ( info->pieceSize % tor->blockSize ) == 0 );
440    t = info->pieceCount - 1;
441    t *= info->pieceSize;
442    t += tor->lastPieceSize;
443    assert( t == info->totalSize );
444    t = tor->blockCount - 1;
445    t *= tor->blockSize;
446    t += tor->lastBlockSize;
447    assert( t == info->totalSize );
448    t = info->pieceCount - 1;
449    t *= tor->blockCountInPiece;
450    t += tor->blockCountInLastPiece;
451    assert( t == (uint64_t)tor->blockCount );
452
453    tor->completion = tr_cpInit( tor );
454
455    tr_torrentInitFilePieces( tor );
456
457    tor->upload         = tr_rcInit();
458    tor->download       = tr_rcInit();
459    tor->swarmSpeed     = tr_rcInit();
460
461    tr_sha1( tor->obfuscatedHash, "req2", 4,
462                                  info->hash, SHA_DIGEST_LENGTH,
463                                  NULL );
464
465    tr_peerMgrAddTorrent( h->peerMgr, tor );
466
467    if( !h->isPortSet )
468        tr_sessionSetPeerPort( h, TR_DEFAULT_PORT );
469
470    assert( !tor->downloadedCur );
471    assert( !tor->uploadedCur );
472
473    tor->error   = TR_OK;
474
475    tor->checkedPieces = tr_bitfieldNew( tor->info.pieceCount );
476    tr_torrentUncheck( tor );
477
478    tor->addedDate = time( NULL ); /* this is a default value to be
479                                      overwritten by the resume file */
480    loaded = tr_torrentLoadResume( tor, ~0, ctor );
481   
482    doStart = tor->isRunning;
483    tor->isRunning = 0;
484
485    if( !(loaded & TR_FR_SPEEDLIMIT ) ) {
486        tr_torrentSetSpeedLimit( tor, TR_UP,
487                tr_sessionGetSpeedLimit( tor->handle, TR_UP ) );
488        tr_torrentSetSpeedLimit( tor, TR_DOWN,
489                tr_sessionGetSpeedLimit( tor->handle, TR_DOWN ) );
490    }
491
492    tor->cpStatus = tr_cpGetStatus( tor->completion );
493
494    tor->tracker = tr_trackerNew( tor );
495    tor->trackerSubscription = tr_trackerSubscribe( tor->tracker, onTrackerResponse, tor );
496
497    {
498        tr_torrent * it = NULL;
499        tr_torrent * last = NULL;
500        while(( it = tr_torrentNext( h, it )))
501            last = it;
502        if( !last )
503            h->torrentList = tor;
504        else
505            last->next = tor;
506        ++h->torrentCount;
507    }
508
509    tr_globalUnlock( h );
510
511    /* maybe save our own copy of the metainfo */
512    if( tr_ctorGetSave( ctor ) ) {
513        const tr_benc * val;
514        if( !tr_ctorGetMetainfo( ctor, &val ) ) {
515            const char * filename = tor->info.torrent;
516            tr_bencSaveFile( filename, val );
517            tr_sessionSetTorrentFile( tor->handle, tor->info.hashString, filename );
518        }
519    }
520
521    tr_metainfoMigrate( h, &tor->info );
522
523    if( doStart )
524        torrentStart( tor, FALSE );
525}
526
527int
528tr_torrentParse( const tr_handle  * handle,
529                 const tr_ctor    * ctor,
530                 tr_info          * setmeInfo )
531{
532    int err = 0;
533    int doFree;
534    tr_info tmp;
535    const tr_benc * metainfo;
536
537    if( setmeInfo == NULL )
538        setmeInfo = &tmp;
539    memset( setmeInfo, 0, sizeof( tr_info ) );
540
541    if( !err && tr_ctorGetMetainfo( ctor, &metainfo ) )
542        return TR_EINVALID;
543
544    err = tr_metainfoParse( handle, setmeInfo, metainfo );
545    doFree = !err && ( setmeInfo == &tmp );
546
547    if( !err && tr_torrentExists( handle, setmeInfo->hash ) )
548        err = TR_EDUPLICATE;
549
550    if( doFree )
551        tr_metainfoFree( setmeInfo );
552
553    return err;
554}
555
556tr_torrent *
557tr_torrentNew( tr_handle      * handle,
558               const tr_ctor  * ctor,
559               int            * setmeError )
560{
561    int err;
562    tr_info tmpInfo;
563    tr_torrent * tor = NULL;
564
565    err = tr_torrentParse( handle, ctor, &tmpInfo );
566    if( !err ) {
567        tor = tr_new0( tr_torrent, 1 );
568        tor->info = tmpInfo;
569        torrentRealInit( handle, tor, ctor );
570    } else if( setmeError ) {
571        *setmeError = err;
572    }
573
574    return tor;
575}
576
577/**
578***
579**/
580
581void
582tr_torrentSetDownloadDir( tr_torrent * tor, const char * path )
583{
584    if( !path || !tor->downloadDir || strcmp( path, tor->downloadDir ) )
585    {
586        tr_free( tor->downloadDir );
587        tor->downloadDir = tr_strdup( path );
588        tr_torrentSaveResume( tor );
589    }
590}
591
592const char*
593tr_torrentGetDownloadDir( const tr_torrent * tor )
594{
595    return tor->downloadDir;
596}
597
598void
599tr_torrentChangeMyPort( tr_torrent * tor )
600{
601    if( tor->tracker )
602        tr_trackerChangeMyPort( tor->tracker );
603}
604
605int
606tr_torrentIsPrivate( const tr_torrent * tor )
607{
608    return tor
609        && tor->info.isPrivate;
610}
611
612int
613tr_torrentAllowsPex( const tr_torrent * tor )
614{
615    return tor
616        && tor->handle->isPexEnabled
617        && !tr_torrentIsPrivate( tor );
618}
619
620static void
621tr_torrentManualUpdateImpl( void * vtor )
622{
623    tr_torrent * tor = vtor;
624    if( tor->isRunning )
625        tr_trackerReannounce( tor->tracker );
626}
627void
628tr_torrentManualUpdate( tr_torrent * tor )
629{
630    tr_runInEventThread( tor->handle, tr_torrentManualUpdateImpl, tor );
631}
632int
633tr_torrentCanManualUpdate( const tr_torrent * tor )
634{
635    return ( tor )
636        && ( tor->isRunning )
637        && ( tr_trackerCanManualAnnounce( tor->tracker ) );
638}
639
640/* rcRate's averaging code can make it appear that we're
641 * still sending bytes after a torrent stops or all the
642 * peers disconnect, so short-circuit that appearance here */
643void
644tr_torrentGetRates( const tr_torrent * tor,
645                    float            * toClient,
646                    float            * toPeer)
647{
648    const int showSpeed = tor->isRunning
649        && tr_peerMgrHasConnections( tor->handle->peerMgr, tor->info.hash );
650
651    if( toClient )
652        *toClient = showSpeed ? tr_rcRate( tor->download ) : 0.0;
653    if( toPeer )
654        *toPeer = showSpeed ? tr_rcRate( tor->upload ) : 0.0;
655}
656const tr_info *
657tr_torrentInfo( const tr_torrent * tor )
658{
659    return tor ? &tor->info : NULL;
660}
661
662const tr_stat *
663tr_torrentStatCached( tr_torrent * tor )
664{
665    const time_t now = time( NULL );
666
667    return tor && ( now == tor->lastStatTime ) ? &tor->stats
668                                               : tr_torrentStat( tor );
669}
670
671tr_torrent_status
672tr_torrentGetStatus( tr_torrent * tor )
673{
674    tr_torrentRecheckCompleteness( tor );
675
676    if( tor->verifyState == TR_VERIFY_NOW )
677        return TR_STATUS_CHECK;
678    if( tor->verifyState == TR_VERIFY_WAIT )
679        return TR_STATUS_CHECK_WAIT;
680    if( !tor->isRunning )
681        return TR_STATUS_STOPPED;
682    if( tor->cpStatus == TR_CP_INCOMPLETE )
683        return TR_STATUS_DOWNLOAD;
684
685    return TR_STATUS_SEED;
686}
687
688const tr_stat *
689tr_torrentStat( tr_torrent * tor )
690{
691    tr_stat * s;
692    struct tr_tracker * tc;
693    const tr_tracker_info * ti;
694    int usableSeeds = 0;
695
696    if( !tor )
697        return NULL;
698
699    tr_torrentLock( tor );
700
701    tor->lastStatTime = time( NULL );
702
703    s = &tor->stats;
704    s->id = tor->uniqueId;
705    s->status = tr_torrentGetStatus( tor );
706    s->error  = tor->error;
707    memcpy( s->errorString, tor->errorString,
708            sizeof( s->errorString ) );
709
710    tc = tor->tracker;
711    ti = tr_trackerGetAddress( tor->tracker );
712    s->announceURL = ti ? ti->announce : NULL;
713    s->scrapeURL   = ti ? ti->scrape   : NULL;
714    tr_trackerStat( tc, s );
715    tr_trackerGetCounts( tc, &s->timesCompleted,
716                             &s->leechers, 
717                             &s->seeders );
718    tr_peerMgrTorrentStats( tor->handle->peerMgr,
719                            tor->info.hash,
720                            &s->peersKnown,
721                            &s->peersConnected,
722                               &usableSeeds,
723                            &s->webseedsSendingToUs,
724                            &s->peersSendingToUs,
725                            &s->peersGettingFromUs,
726                             s->peersFrom );
727
728    usableSeeds += tor->info.webseedCount;
729
730    s->percentComplete = tr_cpPercentComplete ( tor->completion );
731
732    s->percentDone = tr_cpPercentDone( tor->completion );
733    s->leftUntilDone = tr_cpLeftUntilDone( tor->completion );
734    s->sizeWhenDone = tr_cpSizeWhenDone( tor->completion );
735
736    s->recheckProgress = s->status == TR_STATUS_CHECK
737        ? 1.0 - (tr_torrentCountUncheckedPieces( tor ) / (double) tor->info.pieceCount)
738        : 0.0;
739
740    tr_torrentGetRates( tor, &s->rateDownload, &s->rateUpload );
741
742    s->swarmSpeed = tr_rcRate( tor->swarmSpeed );
743   
744    s->activityDate = tor->activityDate;
745    s->addedDate    = tor->addedDate;
746    s->doneDate     = tor->doneDate;
747    s->startDate    = tor->startDate;
748
749    s->corruptEver     = tor->corruptCur    + tor->corruptPrev;
750    s->downloadedEver  = tor->downloadedCur + tor->downloadedPrev;
751    s->uploadedEver    = tor->uploadedCur   + tor->uploadedPrev;
752    s->haveValid       = tr_cpHaveValid( tor->completion );
753    s->haveUnchecked   = tr_cpHaveTotal( tor->completion ) - s->haveValid;
754
755
756    if( usableSeeds > 0 )
757    {
758        s->desiredAvailable = s->leftUntilDone;
759    }
760    else if( !s->leftUntilDone || !s->peersConnected )
761    {
762        s->desiredAvailable = 0;
763    }
764    else
765    {
766        tr_piece_index_t i;
767        tr_bitfield * peerPieces = tr_peerMgrGetAvailable( tor->handle->peerMgr,
768                                                           tor->info.hash );
769        s->desiredAvailable = 0;
770        for( i=0; i<tor->info.pieceCount; ++i )
771            if( !tor->info.pieces[i].dnd && tr_bitfieldHas( peerPieces, i ) )
772                s->desiredAvailable += tr_cpMissingBlocksInPiece( tor->completion, i );
773        s->desiredAvailable *= tor->blockSize;
774        tr_bitfieldFree( peerPieces );
775    }
776
777    if( s->leftUntilDone > s->desiredAvailable )
778        s->eta = TR_ETA_NOT_AVAIL;
779    else if( s->rateDownload < 0.1 )
780        s->eta = TR_ETA_UNKNOWN;
781    else
782        s->eta = s->leftUntilDone / s->rateDownload / 1024.0;
783
784    s->ratio = tr_getRatio( s->uploadedEver,
785                            s->downloadedEver ? s->downloadedEver : s->haveValid );
786   
787    tr_torrentUnlock( tor );
788
789    return s;
790}
791
792/***
793****
794***/
795
796static uint64_t
797fileBytesCompleted ( const tr_torrent * tor, tr_file_index_t fileIndex )
798{
799    const tr_file * file     =  &tor->info.files[fileIndex];
800    const tr_block_index_t firstBlock       =  file->offset / tor->blockSize;
801    const uint64_t firstBlockOffset =  file->offset % tor->blockSize;
802    const uint64_t lastOffset       =  file->length ? (file->length-1) : 0;
803    const tr_block_index_t lastBlock        = (file->offset + lastOffset) / tor->blockSize;
804    const uint64_t lastBlockOffset  = (file->offset + lastOffset) % tor->blockSize;
805    uint64_t haveBytes = 0;
806
807    assert( tor );
808    assert( fileIndex < tor->info.fileCount );
809    assert( file->offset + file->length <= tor->info.totalSize );
810    assert( ( firstBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
811    assert( ( lastBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
812    assert( firstBlock <= lastBlock );
813    assert( tr_torBlockPiece( tor, firstBlock ) == file->firstPiece );
814    assert( tr_torBlockPiece( tor, lastBlock ) == file->lastPiece );
815
816    if( firstBlock == lastBlock )
817    {
818        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
819            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
820    }
821    else
822    {
823        tr_block_index_t i;
824
825        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
826            haveBytes += tor->blockSize - firstBlockOffset;
827
828        for( i=firstBlock+1; i<lastBlock; ++i )
829            if( tr_cpBlockIsComplete( tor->completion, i ) )
830               haveBytes += tor->blockSize;
831
832        if( tr_cpBlockIsComplete( tor->completion, lastBlock ) )
833            haveBytes += lastBlockOffset + 1;
834    }
835
836    return haveBytes;
837}
838
839tr_file_stat *
840tr_torrentFiles( const tr_torrent * tor, tr_file_index_t * fileCount )
841{
842    tr_file_index_t i;
843    const tr_file_index_t n = tor->info.fileCount;
844    tr_file_stat * files = tr_new0( tr_file_stat, n );
845    tr_file_stat * walk = files;
846
847    for( i=0; i<n; ++i, ++walk ) {
848        const uint64_t b = fileBytesCompleted( tor, i );
849        walk->bytesCompleted = b;
850        walk->progress = tr_getRatio( b, tor->info.files[i].length );
851    }
852
853    if( fileCount )
854        *fileCount = n;
855
856    return files;
857}
858
859void
860tr_torrentFilesFree( tr_file_stat * files, tr_file_index_t fileCount UNUSED )
861{
862    tr_free( files );
863}
864
865/***
866****
867***/
868
869float*
870tr_torrentWebSpeeds( const tr_torrent * tor )
871{
872    return tor ? tr_peerMgrWebSpeeds( tor->handle->peerMgr, tor->info.hash )
873               : NULL;
874}
875   
876
877tr_peer_stat *
878tr_torrentPeers( const tr_torrent * tor, int * peerCount )
879{
880    tr_peer_stat * ret = NULL;
881
882    if( tor )
883        ret = tr_peerMgrPeerStats( tor->handle->peerMgr,
884                                   tor->info.hash, peerCount );
885
886    return ret;
887}
888
889void
890tr_torrentPeersFree( tr_peer_stat * peers, int peerCount UNUSED )
891{
892    tr_free( peers );
893}
894
895void
896tr_torrentAvailability( const tr_torrent * tor, int8_t * tab, int size )
897{
898    tr_peerMgrTorrentAvailability( tor->handle->peerMgr,
899                                   tor->info.hash,
900                                   tab, size );
901}
902
903void
904tr_torrentAmountFinished( const tr_torrent * tor, float * tab, int size )
905{
906    tr_torrentLock( tor );
907    tr_cpGetAmountDone( tor->completion, tab, size );
908    tr_torrentUnlock( tor );
909}
910
911void
912tr_torrentResetTransferStats( tr_torrent * tor )
913{
914    tr_torrentLock( tor );
915
916    tor->downloadedPrev += tor->downloadedCur;
917    tor->downloadedCur   = 0;
918    tor->uploadedPrev   += tor->uploadedCur;
919    tor->uploadedCur     = 0;
920    tor->corruptPrev    += tor->corruptCur;
921    tor->corruptCur      = 0;
922
923    tr_torrentUnlock( tor );
924}
925
926
927void
928tr_torrentSetHasPiece( tr_torrent * tor, tr_piece_index_t pieceIndex, int has )
929{
930    tr_torrentLock( tor );
931
932    assert( tor );
933    assert( pieceIndex < tor->info.pieceCount );
934
935    if( has )
936        tr_cpPieceAdd( tor->completion, pieceIndex );
937    else
938        tr_cpPieceRem( tor->completion, pieceIndex );
939
940    tr_torrentUnlock( tor );
941}
942
943/***
944****
945***/
946
947static void
948freeTorrent( tr_torrent * tor )
949{
950    tr_torrent * t;
951    tr_handle * h = tor->handle;
952    tr_info * inf = &tor->info;
953
954    assert( tor );
955    assert( !tor->isRunning );
956
957    tr_globalLock( h );
958
959    tr_peerMgrRemoveTorrent( h->peerMgr, tor->info.hash );
960
961    tr_cpClose( tor->completion );
962
963    tr_rcClose( tor->upload );
964    tr_rcClose( tor->download );
965    tr_rcClose( tor->swarmSpeed );
966
967    tr_trackerUnsubscribe( tor->tracker, tor->trackerSubscription );
968    tr_trackerFree( tor->tracker );
969    tor->tracker = NULL;
970
971    tr_bitfieldFree( tor->checkedPieces );
972
973    tr_free( tor->downloadDir );
974
975    if( tor == h->torrentList )
976        h->torrentList = tor->next;
977    else for( t=h->torrentList; t!=NULL; t=t->next ) {
978        if( t->next == tor ) {
979            t->next = tor->next;
980            break;
981        }
982    }
983
984    assert( h->torrentCount >= 1 );
985    h->torrentCount--;
986
987    tr_metainfoFree( inf );
988    tr_free( tor );
989
990    tr_globalUnlock( h );
991}
992
993/**
994***  Start/Stop Callback
995**/
996
997static void
998checkAndStartImpl( void * vtor )
999{
1000    tr_torrent * tor = vtor;
1001
1002    tr_globalLock( tor->handle );
1003
1004    tor->isRunning  = 1;
1005    *tor->errorString = '\0';
1006    tr_torrentResetTransferStats( tor );
1007    tor->cpStatus = tr_cpGetStatus( tor->completion );
1008    tr_torrentSaveResume( tor );
1009    tor->startDate = time( NULL );
1010    tr_trackerStart( tor->tracker );
1011    tr_peerMgrStartTorrent( tor->handle->peerMgr, tor->info.hash );
1012
1013    tr_globalUnlock( tor->handle );
1014}
1015
1016static void
1017checkAndStartCB( tr_torrent * tor )
1018{
1019    tr_runInEventThread( tor->handle, checkAndStartImpl, tor );
1020}
1021
1022static void
1023torrentStart( tr_torrent * tor, int reloadProgress )
1024{
1025    tr_globalLock( tor->handle );
1026
1027    if( !tor->isRunning )
1028    {
1029        if( reloadProgress )
1030            tr_torrentLoadResume( tor, TR_FR_PROGRESS, NULL );
1031        tor->isRunning = 1;
1032        tr_verifyAdd( tor, checkAndStartCB );
1033    }
1034
1035    tr_globalUnlock( tor->handle );
1036}
1037
1038void
1039tr_torrentStart( tr_torrent * tor )
1040{
1041    if( tor )
1042        torrentStart( tor, TRUE );
1043}
1044
1045static void
1046torrentRecheckDoneImpl( void * vtor )
1047{
1048    tr_torrentRecheckCompleteness( vtor );
1049}
1050static void
1051torrentRecheckDoneCB( tr_torrent * tor )
1052{
1053    tr_runInEventThread( tor->handle, torrentRecheckDoneImpl, tor );
1054}
1055void
1056tr_torrentVerify( tr_torrent * tor )
1057{
1058    tr_verifyRemove( tor );
1059
1060    tr_globalLock( tor->handle );
1061
1062    tr_torrentUncheck( tor );
1063    tr_verifyAdd( tor, torrentRecheckDoneCB );
1064
1065    tr_globalUnlock( tor->handle );
1066}
1067
1068
1069static void
1070stopTorrent( void * vtor )
1071{
1072    tr_file_index_t i;
1073
1074    tr_torrent * tor = vtor;
1075    tr_verifyRemove( tor );
1076    tr_peerMgrStopTorrent( tor->handle->peerMgr, tor->info.hash );
1077    tr_trackerStop( tor->tracker );
1078
1079    for( i=0; i<tor->info.fileCount; ++i )
1080    {
1081        char path[MAX_PATH_LENGTH];
1082        const tr_file * file = &tor->info.files[i];
1083        tr_buildPath( path, sizeof(path), tor->downloadDir, file->name, NULL );
1084        tr_fdFileClose( path );
1085    }
1086}
1087
1088void
1089tr_torrentStop( tr_torrent * tor )
1090{
1091    if( tor )
1092    {
1093        tr_globalLock( tor->handle );
1094
1095        tor->isRunning = 0;
1096        if( !tor->isDeleting )
1097            tr_torrentSaveResume( tor );
1098        tr_runInEventThread( tor->handle, stopTorrent, tor );
1099
1100        tr_globalUnlock( tor->handle );
1101    }
1102}
1103
1104static void
1105closeTorrent( void * vtor )
1106{
1107    tr_torrent * tor = vtor;
1108    tr_torrentSaveResume( tor );
1109    tor->isRunning = 0;
1110    stopTorrent( tor );
1111    if( tor->isDeleting ) {
1112        tr_metainfoRemoveSaved( tor->handle, &tor->info );
1113        tr_torrentRemoveResume( tor );
1114    }
1115    freeTorrent( tor );
1116}
1117
1118void
1119tr_torrentFree( tr_torrent * tor )
1120{
1121    if( tor )
1122    {
1123        tr_handle * handle = tor->handle;
1124        tr_globalLock( handle );
1125
1126        tr_torrentClearStatusCallback( tor );
1127        tr_runInEventThread( handle, closeTorrent, tor );
1128
1129        tr_globalUnlock( handle );
1130    }
1131}
1132
1133void
1134tr_torrentRemove( tr_torrent * tor )
1135{
1136    tor->isDeleting = 1;
1137    tr_torrentFree( tor );
1138}
1139
1140
1141/**
1142***  Completeness
1143**/
1144
1145static const char *
1146getCompletionString( int type )
1147{
1148    switch( type )
1149    {
1150        /* Translators: this is a minor point that's safe to skip over, but FYI:
1151           "Complete" and "Done" are specific, different terms in Transmission:
1152           "Complete" means we've downloaded every file in the torrent.
1153           "Done" means we're done downloading the files we wanted, but NOT all that exist */
1154        case TR_CP_DONE:     return _( "Done" );
1155        case TR_CP_COMPLETE: return _( "Complete" );
1156        default:             return _( "Incomplete" );
1157    }
1158}
1159
1160static void
1161fireStatusChange( tr_torrent * tor, cp_status_t status )
1162{
1163    assert( tor );
1164    assert( status==TR_CP_INCOMPLETE || status==TR_CP_DONE || status==TR_CP_COMPLETE );
1165
1166    if( tor->status_func )
1167        tor->status_func( tor, status, tor->status_func_user_data );
1168}
1169
1170void
1171tr_torrentSetStatusCallback( tr_torrent             * tor,
1172                             tr_torrent_status_func   func,
1173                             void                   * user_data )
1174{
1175    assert( tor );
1176    tor->status_func = func;
1177    tor->status_func_user_data = user_data;
1178}
1179
1180void
1181tr_torrentClearStatusCallback( tr_torrent * torrent )
1182{
1183    tr_torrentSetStatusCallback( torrent, NULL, NULL );
1184}
1185
1186void
1187tr_torrentRecheckCompleteness( tr_torrent * tor )
1188{
1189    cp_status_t cpStatus;
1190
1191    tr_torrentLock( tor );
1192
1193    cpStatus = tr_cpGetStatus( tor->completion );
1194
1195    if( cpStatus != tor->cpStatus )
1196    {
1197        const int recentChange = tor->downloadedCur != 0;
1198
1199        if( recentChange )
1200        {
1201            tr_torinf( tor, _( "State changed from \"%1$s\" to \"%2$s\"" ),
1202                            getCompletionString( tor->cpStatus ),
1203                            getCompletionString( cpStatus ) );
1204        }
1205
1206        tor->cpStatus = cpStatus;
1207        fireStatusChange( tor, cpStatus );
1208
1209        if( recentChange && ( cpStatus == TR_CP_COMPLETE ) )
1210        {
1211            tr_trackerCompleted( tor->tracker );
1212
1213            tor->doneDate = time( NULL );
1214        }
1215
1216        tr_torrentSaveResume( tor );
1217    }
1218
1219    tr_torrentUnlock( tor );
1220}
1221
1222int
1223tr_torrentIsSeed( const tr_torrent * tor )
1224{
1225    return tor->cpStatus==TR_CP_COMPLETE || tor->cpStatus==TR_CP_DONE;
1226}
1227
1228/**
1229***  File priorities
1230**/
1231
1232void
1233tr_torrentInitFilePriority( tr_torrent      * tor,
1234                            tr_file_index_t   fileIndex,
1235                            tr_priority_t     priority )
1236{
1237    tr_piece_index_t i;
1238    tr_file * file;
1239
1240    assert( tor );
1241    assert( fileIndex < tor->info.fileCount );
1242    assert( priority==TR_PRI_LOW || priority==TR_PRI_NORMAL || priority==TR_PRI_HIGH );
1243
1244    file = &tor->info.files[fileIndex];
1245    file->priority = priority;
1246    for( i=file->firstPiece; i<=file->lastPiece; ++i )
1247      tor->info.pieces[i].priority = calculatePiecePriority( tor, i, fileIndex );
1248}
1249
1250void
1251tr_torrentSetFilePriorities( tr_torrent       * tor,
1252                             tr_file_index_t  * files,
1253                             tr_file_index_t    fileCount,
1254                             tr_priority_t      priority )
1255{
1256    tr_file_index_t i;
1257    tr_torrentLock( tor );
1258
1259    for( i=0; i<fileCount; ++i )
1260        tr_torrentInitFilePriority( tor, files[i], priority );
1261
1262    tr_torrentSaveResume( tor );
1263    tr_torrentUnlock( tor );
1264}
1265
1266tr_priority_t
1267tr_torrentGetFilePriority( const tr_torrent *  tor, tr_file_index_t file )
1268{
1269    tr_priority_t ret;
1270
1271    tr_torrentLock( tor );
1272    assert( tor );
1273    assert( file < tor->info.fileCount );
1274    ret = tor->info.files[file].priority;
1275    tr_torrentUnlock( tor );
1276
1277    return ret;
1278}
1279
1280tr_priority_t*
1281tr_torrentGetFilePriorities( const tr_torrent * tor )
1282{
1283    tr_file_index_t i;
1284    tr_priority_t * p;
1285
1286    tr_torrentLock( tor );
1287    p = tr_new0( tr_priority_t, tor->info.fileCount );
1288    for( i=0; i<tor->info.fileCount; ++i )
1289        p[i] = tor->info.files[i].priority;
1290    tr_torrentUnlock( tor );
1291
1292    return p;
1293}
1294
1295/**
1296***  File DND
1297**/
1298
1299int
1300tr_torrentGetFileDL( const tr_torrent * tor,
1301                     tr_file_index_t    file )
1302{
1303    int doDownload;
1304    tr_torrentLock( tor );
1305
1306    assert( file < tor->info.fileCount );
1307    doDownload = !tor->info.files[file].dnd;
1308
1309    tr_torrentUnlock( tor );
1310    return doDownload != 0;
1311}
1312
1313static void
1314setFileDND( tr_torrent      * tor,
1315            tr_file_index_t   fileIndex,
1316            int               doDownload )
1317{
1318    tr_file * file;
1319    const int dnd = !doDownload;
1320    tr_piece_index_t firstPiece, firstPieceDND;
1321    tr_piece_index_t lastPiece, lastPieceDND;
1322    tr_file_index_t i;
1323
1324    file = &tor->info.files[fileIndex];
1325    file->dnd = dnd;
1326    firstPiece = file->firstPiece;
1327    lastPiece = file->lastPiece;
1328
1329    /* can't set the first piece to DND unless
1330       every file using that piece is DND */
1331    firstPieceDND = dnd;
1332    if( fileIndex > 0 ) {
1333        for( i=fileIndex-1; firstPieceDND; --i ) {
1334            if( tor->info.files[i].lastPiece != firstPiece )
1335                break;
1336            firstPieceDND = tor->info.files[i].dnd;
1337            if( !i )
1338                break;
1339        }
1340    }
1341
1342    /* can't set the last piece to DND unless
1343       every file using that piece is DND */
1344    lastPieceDND = dnd;
1345    for( i=fileIndex+1; lastPieceDND && i<tor->info.fileCount; ++i ) {
1346        if( tor->info.files[i].firstPiece != lastPiece )
1347            break;
1348        lastPieceDND = tor->info.files[i].dnd;
1349    }
1350
1351    if( firstPiece == lastPiece )
1352    {
1353        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
1354    }
1355    else
1356    {
1357        tr_piece_index_t pp;
1358        tor->info.pieces[firstPiece].dnd = firstPieceDND;
1359        tor->info.pieces[lastPiece].dnd = lastPieceDND;
1360        for( pp=firstPiece+1; pp<lastPiece; ++pp )
1361            tor->info.pieces[pp].dnd = dnd;
1362    }
1363}
1364
1365void
1366tr_torrentInitFileDLs ( tr_torrent       * tor,
1367                        tr_file_index_t  * files,
1368                        tr_file_index_t    fileCount,
1369                        int                doDownload )
1370{
1371    tr_file_index_t i;
1372    tr_torrentLock( tor );
1373
1374    for( i=0; i<fileCount; ++i )
1375        setFileDND( tor, files[i], doDownload );
1376    tr_cpInvalidateDND ( tor->completion );
1377
1378    tr_torrentUnlock( tor );
1379}
1380
1381void
1382tr_torrentSetFileDLs ( tr_torrent      * tor,
1383                       tr_file_index_t * files,
1384                       tr_file_index_t   fileCount,
1385                       int               doDownload )
1386{
1387    tr_torrentLock( tor );
1388    tr_torrentInitFileDLs( tor, files, fileCount, doDownload );
1389    tr_torrentSaveResume( tor );
1390    tr_torrentUnlock( tor );
1391}
1392
1393/***
1394****
1395***/
1396
1397void
1398tr_torrentSetPeerLimit( tr_torrent  * tor,
1399                        uint16_t      maxConnectedPeers )
1400{
1401    tor->maxConnectedPeers = maxConnectedPeers;
1402}
1403
1404uint16_t
1405tr_torrentGetPeerLimit( const tr_torrent  * tor )
1406{
1407    return tor->maxConnectedPeers;
1408}
1409
1410/***
1411****
1412***/
1413
1414tr_block_index_t
1415_tr_block( const tr_torrent  * tor,
1416           tr_piece_index_t    index,
1417           uint32_t            offset )
1418{
1419    const tr_info * inf = &tor->info;
1420    tr_block_index_t ret;
1421    ret = index;
1422    ret *= ( inf->pieceSize / tor->blockSize );
1423    ret += offset / tor->blockSize;
1424    return ret;
1425}
1426
1427int
1428tr_torrentReqIsValid( const tr_torrent * tor,
1429                      tr_piece_index_t   index,
1430                      uint32_t           offset,
1431                      uint32_t           length )
1432{
1433    int err = 0;
1434
1435    if( index >= tor->info.pieceCount )
1436        err = 1;
1437    else if ( offset >= tr_torPieceCountBytes( tor, index ) )
1438        err = 2;
1439    else if( length > MAX_BLOCK_SIZE )
1440        err = 3;
1441    else if( tr_pieceOffset( tor, index, offset, length ) > tor->info.totalSize )
1442        err = 4;
1443
1444if( err ) fprintf( stderr, "index %lu offset %lu length %lu err %d\n", (unsigned long)index, (unsigned long)offset, (unsigned long)length, err );
1445
1446    return !err;
1447}
1448
1449
1450uint64_t
1451tr_pieceOffset( const tr_torrent * tor,
1452                tr_piece_index_t   index,
1453                uint32_t           offset,
1454                uint32_t           length )
1455{
1456    uint64_t ret;
1457    ret = tor->info.pieceSize;
1458    ret *= index;
1459    ret += offset;
1460    ret += length;
1461    return ret;
1462}
1463
1464/***
1465****
1466***/
1467
1468int
1469tr_torrentIsPieceChecked( const tr_torrent * tor, tr_piece_index_t piece )
1470{
1471    return tr_bitfieldHas( tor->checkedPieces, piece );
1472}
1473
1474void
1475tr_torrentSetPieceChecked( tr_torrent * tor, tr_piece_index_t piece, int isChecked )
1476{
1477    if( isChecked )
1478        tr_bitfieldAdd( tor->checkedPieces, piece );
1479    else
1480        tr_bitfieldRem( tor->checkedPieces, piece );
1481}
1482
1483void
1484tr_torrentSetFileChecked( tr_torrent * tor, tr_file_index_t fileIndex, int isChecked )
1485{
1486    const tr_file * file = &tor->info.files[fileIndex];
1487    const tr_piece_index_t begin = file->firstPiece;
1488    const tr_piece_index_t end = file->lastPiece + 1;
1489
1490    if( isChecked )
1491        tr_bitfieldAddRange ( tor->checkedPieces, begin, end );
1492    else
1493        tr_bitfieldRemRange ( tor->checkedPieces, begin, end );
1494}
1495
1496int
1497tr_torrentIsFileChecked( const tr_torrent * tor, tr_file_index_t fileIndex )
1498{
1499    const tr_file * file = &tor->info.files[fileIndex];
1500    const tr_piece_index_t begin = file->firstPiece;
1501    const tr_piece_index_t end = file->lastPiece + 1;
1502    tr_piece_index_t i;
1503    int isChecked = TRUE;
1504
1505    for( i=begin; isChecked && i<end; ++i )
1506        if( !tr_torrentIsPieceChecked( tor, i ) )
1507            isChecked = FALSE;
1508
1509    return isChecked;
1510}
1511
1512void
1513tr_torrentUncheck( tr_torrent * tor )
1514{
1515    tr_bitfieldRemRange ( tor->checkedPieces, 0, tor->info.pieceCount );
1516}
1517
1518int
1519tr_torrentCountUncheckedPieces( const tr_torrent * tor )
1520{
1521    return tor->info.pieceCount - tr_bitfieldCountTrueBits( tor->checkedPieces );
1522}
1523
1524time_t*
1525tr_torrentGetMTimes( const tr_torrent * tor, int * setme_n )
1526{
1527    int i;
1528    const int n = tor->info.fileCount;
1529    time_t * m = tr_new0( time_t, n );
1530
1531    for( i=0; i<n; ++i ) {
1532        char fname[MAX_PATH_LENGTH];
1533        struct stat sb;
1534        tr_buildPath( fname, sizeof(fname),
1535                      tor->downloadDir, tor->info.files[i].name, NULL );
1536        if ( !stat( fname, &sb ) ) {
1537#ifdef SYS_DARWIN
1538            m[i] = sb.st_mtimespec.tv_sec;
1539#else
1540            m[i] = sb.st_mtime;
1541#endif
1542        }
1543    }
1544
1545    *setme_n = n;
1546    return m;
1547}
1548
1549/***
1550****
1551***/
1552
1553void
1554tr_torrentSetAnnounceList( tr_torrent             * tor,
1555                           const tr_tracker_info  * trackers,
1556                           int                      trackerCount )
1557{
1558    tr_benc metainfo;
1559
1560    /* save to the .torrent file */
1561    if( !tr_bencLoadFile( tor->info.torrent, &metainfo ) )
1562    {
1563        int i;
1564        int prevTier = -1;
1565        tr_benc * tier = NULL;
1566        tr_benc * announceList;
1567        tr_info tmpInfo;
1568
1569        /* remove the old fields */
1570        tr_bencDictRemove( &metainfo, "announce" );
1571        tr_bencDictRemove( &metainfo, "announce-list" );
1572
1573        /* add the new fields */
1574        tr_bencDictAddStr( &metainfo, "announce", trackers[0].announce );
1575        announceList = tr_bencDictAddList( &metainfo, "announce-list", 0 );
1576        for( i=0; i<trackerCount; ++i ) {
1577            if( prevTier != trackers[i].tier ) {
1578                prevTier = trackers[i].tier;
1579                tier = tr_bencListAddList( announceList, 0 );
1580            }
1581            tr_bencListAddStr( tier, trackers[i].announce );
1582        }
1583
1584        /* try to parse it back again, to make sure it's good */
1585        memset( &tmpInfo, 0, sizeof( tr_info ) );
1586        if( !tr_metainfoParse( tor->handle, &tmpInfo, &metainfo ) )
1587        {
1588            /* if it's good, save it and use it */
1589            tr_metainfoFree( &tor->info );
1590            tor->info = tmpInfo;
1591            tr_torrentInitFilePieces( tor );
1592            tr_bencSaveFile( tor->info.torrent, &metainfo );
1593        }
1594
1595        /* cleanup */ 
1596        tr_bencFree( &metainfo );
1597    }
1598}
1599
1600/**
1601***
1602**/
1603
1604/** @deprecated this method will be removed in 1.40 */
1605void
1606tr_torrentSetAddedDate( tr_torrent * tor, time_t t )
1607{
1608    tor->addedDate = t;
1609}
1610
1611/** @deprecated this method will be removed in 1.40 */
1612void
1613tr_torrentSetActivityDate( tr_torrent * tor, time_t t )
1614{
1615    tor->activityDate = t;
1616}
1617
1618/** @deprecated this method will be removed in 1.40 */
1619void
1620tr_torrentSetDoneDate( tr_torrent * tor, time_t t )
1621{
1622    tor->doneDate = t;
1623}
Note: See TracBrowser for help on using the repository browser.