source: trunk/libtransmission/torrent.c @ 7473

Last change on this file since 7473 was 7473, checked in by charles, 14 years ago

(trunk) #1029: When removing local data only remove data from the torrent

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