source: trunk/libtransmission/torrent.c @ 3451

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

huge tracker cleanup for the "no response from tracker" issue

  • Property svn:keywords set to Date Rev Author Id
File size: 32.7 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 3451 2007-10-17 18:59:58Z charles $
3 *
4 * Copyright (c) 2005-2007 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 <assert.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29
30#include <sys/types.h>
31
32#include "transmission.h"
33#include "completion.h"
34#include "crypto.h" /* for tr_sha1 */
35#include "fastresume.h"
36#include "handshake.h"
37#include "inout.h"
38#include "metainfo.h"
39#include "net.h" /* tr_netNtop */
40#include "peer-mgr.h"
41#include "platform.h"
42#include "ratecontrol.h"
43#include "shared.h"
44#include "tracker.h"
45#include "trcompat.h" /* for strlcpy */
46#include "trevent.h"
47#include "utils.h"
48
49/***
50****
51***/
52
53int
54tr_torrentExists( tr_handle       * handle,
55                  const uint8_t   * torrentHash )
56{
57    return tr_torrentFindFromHash( handle, torrentHash ) != NULL;
58}
59
60tr_torrent*
61tr_torrentFindFromHash( tr_handle      * handle,
62                        const uint8_t  * torrentHash )
63{
64    tr_torrent * tor;
65
66    for( tor = handle->torrentList; tor; tor = tor->next )
67        if( !memcmp( tor->info.hash, torrentHash, SHA_DIGEST_LENGTH ) )
68            return tor;
69
70    return NULL;
71}
72
73tr_torrent*
74tr_torrentFindFromObfuscatedHash( tr_handle      * handle,
75                                  const uint8_t  * obfuscatedTorrentHash )
76{
77    tr_torrent * tor;
78
79    for( tor = handle->torrentList; tor; tor = tor->next )
80        if( !memcmp( tor->obfuscatedHash, obfuscatedTorrentHash, SHA_DIGEST_LENGTH ) )
81            return tor;
82
83    return NULL;
84}
85
86/***
87****  LOCKS
88***/
89
90void
91tr_torrentLock( const tr_torrent * tor )
92{
93    tr_globalLock( tor->handle );
94}
95
96void
97tr_torrentUnlock( const tr_torrent * tor )
98{
99    tr_globalUnlock( tor->handle );
100}
101
102/***
103****  PER-TORRENT UL / DL SPEEDS
104***/
105
106void
107tr_torrentSetSpeedMode( tr_torrent   * tor,
108                        int            up_or_down,
109                        tr_speedlimit  mode )
110{
111    tr_speedlimit * limit = up_or_down==TR_UP
112        ? &tor->uploadLimitMode
113        : &tor->downloadLimitMode;
114    *limit = mode;
115}
116
117tr_speedlimit
118tr_torrentGetSpeedMode( const tr_torrent * tor,
119                        int                up_or_down)
120{
121    return up_or_down==TR_UP ? tor->uploadLimitMode
122                             : tor->downloadLimitMode;
123}
124
125void
126tr_torrentSetSpeedLimit( tr_torrent   * tor,
127                         int            up_or_down,
128                         int            single_KiB_sec )
129{
130    tr_ratecontrol * rc = up_or_down==TR_UP ? tor->upload : tor->download;
131    tr_rcSetLimit( rc, single_KiB_sec );
132}
133
134int
135tr_torrentGetSpeedLimit( const tr_torrent  * tor,
136                         int                 up_or_down )
137{
138    tr_ratecontrol * rc = up_or_down==TR_UP ? tor->upload : tor->download;
139    return tr_rcGetLimit( rc );
140}
141
142/***
143****
144***/
145
146static void
147onTrackerResponse( void * tracker UNUSED, void * vevent, void * user_data )
148{
149    tr_torrent * tor = user_data;
150    tr_tracker_event * event = vevent;
151
152    switch( event->messageType )
153    {
154        case TR_TRACKER_PEERS:
155            tr_peerMgrAddPeers( tor->handle->peerMgr,
156                                tor->info.hash,
157                                TR_PEER_FROM_TRACKER,
158                                event->peerCompact,
159                                event->peerCount );
160            break;
161
162        case TR_TRACKER_WARNING:
163            tr_err( "Tracker: Warning - %s", event->text );
164            tor->error = TR_ERROR_TC_WARNING;
165            strlcpy( tor->errorString, event->text, sizeof(tor->errorString) );
166            break;
167
168        case TR_TRACKER_ERROR:
169            tr_err( "Tracker: Error - %s", event->text );
170            tor->error = TR_ERROR_TC_ERROR;
171            strlcpy( tor->errorString, event->text, sizeof(tor->errorString) );
172            break;
173
174        case TR_TRACKER_ERROR_CLEAR:
175            tor->error = 0;
176            tor->errorString[0] = '\0';
177            break;
178    }
179}
180
181/***
182****
183****  TORRENT INSTANTIATION
184****
185***/
186
187static int
188getBytePiece( const tr_info * info, uint64_t byteOffset )
189{
190    assert( info != NULL );
191    assert( info->pieceSize != 0 );
192
193    return byteOffset / info->pieceSize;
194}
195
196static void
197initFilePieces ( tr_info * info, int fileIndex )
198{
199    tr_file * file = &info->files[fileIndex];
200    uint64_t firstByte, lastByte;
201
202    assert( info != NULL );
203    assert( 0<=fileIndex && fileIndex<info->fileCount );
204
205    file = &info->files[fileIndex];
206    firstByte = file->offset;
207    lastByte = firstByte + (file->length ? file->length-1 : 0);
208    file->firstPiece = getBytePiece( info, firstByte );
209    file->lastPiece = getBytePiece( info, lastByte );
210}
211
212static tr_priority_t
213calculatePiecePriority ( const tr_torrent * tor,
214                         int                piece )
215{
216    int i;
217    tr_priority_t priority = TR_PRI_NORMAL;
218
219    for( i=0; i<tor->info.fileCount; ++i )
220    {
221        const tr_file * file = &tor->info.files[i];
222        if ( file->firstPiece <= piece
223          && file->lastPiece  >= piece
224          && file->priority   >  priority)
225              priority = file->priority;
226
227        /* when dealing with multimedia files, getting the first and
228           last pieces can sometimes allow you to preview it a bit
229           before it's fully downloaded... */
230        if ( file->priority >= TR_PRI_NORMAL )
231            if ( file->firstPiece == piece || file->lastPiece == piece )
232                priority = TR_PRI_HIGH;
233    }
234
235    return priority;
236}
237
238static void
239tr_torrentInitFilePieces( tr_torrent * tor )
240{
241    int i;
242    uint64_t offset = 0;
243
244    assert( tor != NULL );
245
246    for( i=0; i<tor->info.fileCount; ++i ) {
247      tor->info.files[i].offset = offset;
248      offset += tor->info.files[i].length;
249      initFilePieces( &tor->info, i );
250    }
251
252    for( i=0; i<tor->info.pieceCount; ++i )
253        tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
254}
255
256static void
257torrentRealInit( tr_handle  * h,
258                 tr_torrent * tor,
259                 const char * destination,
260                 int          isPaused )
261{
262    int doStart;
263    uint64_t loaded;
264    uint64_t t;
265    tr_bitfield * uncheckedPieces;
266    tr_info * info = &tor->info;
267   
268    tr_globalLock( h );
269
270    tor->destination = tr_strdup( destination );
271
272    tor->handle   = h;
273    tor->pexDisabled = 0;
274
275    /**
276     * Decide on a block size.  constraints:
277     * (1) most clients decline requests over 16 KiB
278     * (2) pieceSize must be a multiple of block size
279     */
280    tor->blockSize = info->pieceSize;
281    while( tor->blockSize > (1024*16) )
282        tor->blockSize /= 2;
283
284    tor->lastPieceSize = info->totalSize % info->pieceSize;
285
286    if( !tor->lastPieceSize )
287         tor->lastPieceSize = info->pieceSize;
288
289    tor->lastBlockSize = info->totalSize % tor->blockSize;
290
291    if( !tor->lastBlockSize )
292         tor->lastBlockSize = tor->blockSize;
293
294    tor->blockCount =
295        ( info->totalSize + tor->blockSize - 1 ) / tor->blockSize;
296
297    tor->blockCountInPiece =
298        info->pieceSize / tor->blockSize;
299
300    tor->blockCountInLastPiece =
301        ( tor->lastPieceSize + tor->blockSize - 1 ) / tor->blockSize;
302
303    /* check our work */
304    assert( ( info->pieceSize % tor->blockSize ) == 0 );
305    t = info->pieceCount - 1;
306    t *= info->pieceSize;
307    t += tor->lastPieceSize;
308    assert( t == info->totalSize );
309    t = tor->blockCount - 1;
310    t *= tor->blockSize;
311    t += tor->lastBlockSize;
312    assert( t == info->totalSize );
313    t = info->pieceCount - 1;
314    t *= tor->blockCountInPiece;
315    t += tor->blockCountInLastPiece;
316    assert( t == (uint64_t)tor->blockCount );
317
318    tor->completion = tr_cpInit( tor );
319
320    tr_torrentInitFilePieces( tor );
321
322    tor->upload         = tr_rcInit();
323    tor->download       = tr_rcInit();
324    tor->swarmspeed     = tr_rcInit();
325
326    tr_sha1( tor->obfuscatedHash, "req2", 4,
327                                  info->hash, SHA_DIGEST_LENGTH,
328                                  NULL );
329
330    tr_peerMgrAddTorrent( h->peerMgr, tor );
331
332    if( !h->isPortSet )
333        tr_setBindPort( h, TR_DEFAULT_PORT );
334
335    assert( !tor->downloadedCur );
336    assert( !tor->uploadedCur );
337
338    tor->error   = TR_OK;
339
340    uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
341
342    loaded = tr_fastResumeLoad( tor, uncheckedPieces );
343    assert( tor->destination != NULL );
344
345    /* the `paused' flag has highest precedence...
346       after that, the fastresume setting is used...
347       if that's not found, default to RUNNING */
348    if( isPaused )
349        doStart = 0;
350    else if( loaded & TR_FR_RUN )
351        doStart = tor->isRunning;
352    else
353        doStart = 1;
354    tor->isRunning = 0;
355
356    if( tr_bitfieldIsEmpty( uncheckedPieces ) )
357        tr_bitfieldFree( uncheckedPieces );
358    else
359        tor->uncheckedPieces = uncheckedPieces;
360
361
362    if( !(loaded & TR_FR_SPEEDLIMIT ) ) {
363        int limit, enabled;
364        tr_getGlobalSpeedLimit( tor->handle, TR_UP, &enabled, &limit );
365        tr_torrentSetSpeedLimit( tor, TR_UP, limit );
366        tr_getGlobalSpeedLimit( tor->handle, TR_DOWN, &enabled, &limit );
367        tr_torrentSetSpeedLimit( tor, TR_DOWN, limit );
368    }
369
370    tor->cpStatus = tr_cpGetStatus( tor->completion );
371
372    tor->tracker = tr_trackerNew( tor );
373    tor->trackerSubscription = tr_trackerSubscribe( tor->tracker, onTrackerResponse, tor );
374
375    tor->next = h->torrentList;
376    h->torrentList = tor;
377    h->torrentCount++;
378
379    tr_globalUnlock( h );
380
381    if( doStart )
382        tr_torrentStart( tor );
383}
384
385static int
386pathIsInUse ( const tr_handle   * h,
387              const char        * destination,
388              const char        * name )
389{
390    const tr_torrent * tor;
391   
392    for( tor=h->torrentList; tor; tor=tor->next )
393        if( !strcmp( destination, tor->destination )
394         && !strcmp( name, tor->info.name ) )
395            return TRUE;
396
397    return FALSE;
398}
399
400static int
401hashExists( const tr_handle   * h,
402            const uint8_t     * hash )
403{
404    const tr_torrent * tor;
405
406    for( tor=h->torrentList; tor; tor=tor->next )
407        if( !memcmp( hash, tor->info.hash, SHA_DIGEST_LENGTH ) )
408            return TRUE;
409
410    return FALSE;
411}
412
413static int
414infoCanAdd( const tr_handle   * h,
415            const char        * destination,
416            const tr_info     * info )
417{
418    if( hashExists( h, info->hash ) )
419        return TR_EDUPLICATE;
420
421    if( destination && pathIsInUse( h, destination, info->name ) )
422        return TR_EDUPLICATE;
423
424    return TR_OK;
425}
426
427int
428tr_torrentParse( const tr_handle  * h,
429                 const char       * path,
430                 const char       * destination,
431                 tr_info          * setme_info )
432{
433    int ret, doFree;
434    tr_info tmp;
435
436    if( setme_info == NULL )
437        setme_info = &tmp;
438
439    memset( setme_info, 0, sizeof( tr_info ) );
440    ret = tr_metainfoParseFile( setme_info, h->tag, path, FALSE );
441    doFree = !ret && (setme_info == &tmp);
442
443    if( ret == TR_OK )
444        ret = infoCanAdd( h, destination, setme_info );
445
446    if( doFree )
447        tr_metainfoFree( &tmp );
448
449    return ret;
450}
451 
452tr_torrent *
453tr_torrentInit( tr_handle   * h,
454                const char  * path,
455                const char  * destination,
456                int           isPaused,
457                int         * error )
458{
459    int val;
460    int tmpError;
461    tr_torrent * tor = NULL;
462
463    if( !error )
464         error = &tmpError;
465
466    if(( val = tr_torrentParse( h, path, destination, NULL )))
467        *error = val;
468    else if(!(( tor = tr_new0( tr_torrent, 1 ))))
469        *error = TR_EOTHER;
470    else {
471        tr_metainfoParseFile( &tor->info, h->tag, path, TRUE );
472        torrentRealInit( h, tor, destination, isPaused );
473    }
474
475    return tor;
476}
477
478int
479tr_torrentParseHash( const tr_handle  * h,
480                     const char       * hashStr,
481                     const char       * destination,
482                     tr_info          * setme_info )
483{
484    int ret, doFree;
485    tr_info tmp;
486
487    if( setme_info == NULL )
488        setme_info = &tmp;
489
490    memset( setme_info, 0, sizeof( tr_info ) );
491    ret = tr_metainfoParseHash( setme_info, h->tag, hashStr );
492    doFree = !ret && (setme_info == &tmp);
493
494    if( ret == TR_OK )
495        ret = infoCanAdd( h, destination, setme_info );
496
497    if( doFree )
498        tr_metainfoFree( &tmp );
499
500    return ret;
501}
502
503tr_torrent *
504tr_torrentInitSaved( tr_handle    * h,
505                     const char   * hashStr,
506                     const char   * destination,
507                     int            isPaused,
508                     int          * error )
509{
510    int val;
511    int tmpError;
512    tr_torrent * tor = NULL;
513
514    if( !error )
515         error = &tmpError;
516
517    if(( val = tr_torrentParseHash( h, hashStr, destination, NULL )))
518        *error = val;
519    else if(!(( tor = tr_new0( tr_torrent, 1 ))))
520        *error = TR_EOTHER;
521    else {
522        tr_metainfoParseHash( &tor->info, h->tag, hashStr );
523        torrentRealInit( h, tor, destination, isPaused );
524    }
525
526    return tor;
527}
528
529static int
530tr_torrentParseData( const tr_handle  * h,
531                     const uint8_t    * data,
532                     size_t             size,
533                     const char       * destination,
534                     tr_info          * setme_info )
535{
536    int ret, doFree;
537    tr_info tmp;
538
539    if( setme_info == NULL )
540        setme_info = &tmp;
541
542    memset( setme_info, 0, sizeof( tr_info ) );
543    ret = tr_metainfoParseData( setme_info, h->tag, data, size, FALSE );
544    doFree = !ret && (setme_info == &tmp);
545
546    if( ret == TR_OK )
547        ret = infoCanAdd( h, destination, setme_info );
548
549    if( doFree )
550        tr_metainfoFree( &tmp );
551
552    return ret;
553}
554
555tr_torrent *
556tr_torrentInitData( tr_handle      * h,
557                    const uint8_t  * data,
558                    size_t           size,
559                    const char     * destination,
560                    int              isPaused,
561                    int            * error )
562{
563    int val;
564    int tmpError;
565    tr_torrent * tor = NULL;
566
567    if( !error )
568         error = &tmpError;
569
570    if(( val = tr_torrentParseData( h, data, size, destination, NULL )))
571        *error = val;
572    else if(!(( tor = tr_new0( tr_torrent, 1 ))))
573        *error = TR_EOTHER;
574    else {
575        tr_metainfoParseData( &tor->info, h->tag, data, size, TRUE );
576        torrentRealInit( h, tor, destination, isPaused );
577    }
578
579    return tor;
580}
581
582const tr_info *
583tr_torrentInfo( const tr_torrent * tor )
584{
585    return &tor->info;
586}
587
588/***
589****
590***/
591
592#if 0
593int tr_torrentScrape( tr_torrent * tor, int * s, int * l, int * d )
594{
595    return tr_trackerScrape( tor, s, l, d );
596}
597#endif
598
599static int
600saveFastResumeNow( void * vtor )
601{
602    tr_torrent * tor = (tr_torrent *) vtor;
603
604    tr_fastResumeSave( tor );
605    tr_torrentRecheckCompleteness( tor );
606
607    tr_timerFree( &tor->saveTimer );
608    return FALSE;
609}
610
611static void
612saveFastResumeSoon( void * vtor )
613{
614    tr_torrent * tor = (tr_torrent *) vtor;
615
616    if( tor->saveTimer == NULL )
617        tor->saveTimer = tr_timerNew( tor->handle, saveFastResumeNow, tor, 100 );
618}
619
620/**
621***
622**/
623
624void
625tr_torrentSetFolder( tr_torrent * tor, const char * path )
626{
627    tr_free( tor->destination );
628    tor->destination = tr_strdup( path );
629    saveFastResumeSoon( tor );
630}
631
632const char*
633tr_torrentGetFolder( const tr_torrent * tor )
634{
635    return tor->destination;
636}
637
638void
639tr_torrentChangeMyPort( tr_torrent * tor )
640{
641    if( tor->tracker )
642        tr_trackerChangeMyPort( tor->tracker );
643}
644
645int
646tr_torrentIsPexEnabled( const tr_torrent * tor )
647{
648    return !tor->info.isPrivate && !tor->pexDisabled;
649}
650
651void
652tr_torrentDisablePex( tr_torrent * tor, int disable )
653{
654    assert( tor != NULL );
655    assert( disable==0 || disable==1 );
656
657    /* pex is ALWAYS disabled for private torrents */
658    if( tor->info.isPrivate )
659        disable = TRUE;
660
661    tor->pexDisabled = disable;
662}
663
664void
665tr_manualUpdate( tr_torrent * tor )
666{
667    if( tor->isRunning )
668        tr_trackerReannounce( tor->tracker );
669}
670int
671tr_torrentCanManualUpdate( const tr_torrent * tor )
672{
673    return ( tor != NULL )
674        && ( tor->isRunning )
675        && ( tr_trackerCanManualAnnounce( tor->tracker ) );
676}
677
678const tr_stat *
679tr_torrentStat( tr_torrent * tor )
680{
681    tr_stat * s;
682    struct tr_tracker * tc;
683
684    tr_torrentLock( tor );
685
686    tor->statCur = !tor->statCur;
687    s = &tor->stats[tor->statCur];
688
689    s->error  = tor->error;
690    memcpy( s->errorString, tor->errorString,
691            sizeof( s->errorString ) );
692
693    tc = tor->tracker;
694    s->tracker = tr_trackerGetAddress( tor->tracker );
695
696    tr_peerMgrTorrentStats( tor->handle->peerMgr,
697                            tor->info.hash,
698                            &s->peersKnown,
699                            &s->peersConnected,
700                            &s->peersSendingToUs,
701                            &s->peersGettingFromUs,
702                             s->peersFrom );
703
704    s->percentComplete = tr_cpPercentComplete ( tor->completion );
705
706    s->percentDone = tr_cpPercentDone( tor->completion );
707    s->leftUntilDone = tr_cpLeftUntilDone( tor->completion );
708
709    if( tor->recheckState == TR_RECHECK_NOW )
710        s->status = TR_STATUS_CHECK;
711    else if( tor->recheckState == TR_RECHECK_WAIT )
712        s->status = TR_STATUS_CHECK_WAIT;
713    else if( !tor->isRunning )
714        s->status = TR_STATUS_STOPPED;
715    else if( tor->cpStatus == TR_CP_INCOMPLETE )
716        s->status = TR_STATUS_DOWNLOAD;
717    else if( tor->cpStatus == TR_CP_DONE )
718        s->status = TR_STATUS_DONE;
719    else
720        s->status = TR_STATUS_SEED;
721
722    s->recheckProgress = (tor->uncheckedPieces == NULL)
723        ? 0.0
724        : 1.0 - ((double)tr_bitfieldCountTrueBits(tor->uncheckedPieces) / tor->info.pieceCount);
725
726    /* rcRate's averaging code can make it appear that we're
727     * still sending bytes after a torrent stops or all the
728     * peers disconnect, so short-circuit that appearance here */
729    if( tor->isRunning && s->peersConnected ) {
730        s->rateDownload = tr_rcRate( tor->download );
731        s->rateUpload = tr_rcRate( tor->upload );
732    } else {
733        s->rateDownload = 0.0;
734        s->rateUpload = 0.0;
735    }
736   
737    tr_trackerGetCounts( tc,
738                         &s->completedFromTracker,
739                         &s->leechers, 
740                         &s->seeders );
741
742    s->swarmspeed = tr_rcRate( tor->swarmspeed );
743   
744    s->startDate = tor->startDate;
745    s->activityDate = tor->activityDate;
746
747    s->eta = s->rateDownload < 0.1
748        ? -1.0f
749        : (s->leftUntilDone / s->rateDownload / 1024.0);
750
751    s->corruptEver     = tor->corruptCur    + tor->corruptPrev;
752    s->downloadedEver  = tor->downloadedCur + tor->downloadedPrev;
753    s->uploadedEver    = tor->uploadedCur   + tor->uploadedPrev;
754    s->haveValid       = tr_cpHaveValid( tor->completion );
755    s->haveUnchecked   = tr_cpHaveTotal( tor->completion ) - s->haveValid;
756
757
758    {
759        int i;
760        tr_bitfield * available = tr_peerMgrGetAvailable( tor->handle->peerMgr,
761                                                          tor->info.hash );
762        s->desiredSize = 0;
763        s->desiredAvailable = 0;
764
765        for( i=0; i<tor->info.pieceCount; ++i ) {
766            if( !tor->info.pieces[i].dnd ) {
767                s->desiredSize += tor->info.pieceSize;
768                if( tr_bitfieldHas( available, i ) )
769                    s->desiredAvailable += tor->info.pieceSize;
770            }
771        }
772
773        tr_bitfieldFree( available );
774    }
775   
776    s->ratio = s->downloadedEver ? s->uploadedEver / (float)s->downloadedEver
777                                 : TR_RATIO_NA;
778   
779    tr_torrentUnlock( tor );
780
781    return s;
782}
783
784/***
785****
786***/
787
788static uint64_t
789fileBytesCompleted ( const tr_torrent * tor, int fileIndex )
790{
791    const tr_file * file     =  &tor->info.files[fileIndex];
792    const uint64_t firstBlock       =  file->offset / tor->blockSize;
793    const uint64_t firstBlockOffset =  file->offset % tor->blockSize;
794    const uint64_t lastOffset       =  file->length ? (file->length-1) : 0;
795    const uint64_t lastBlock        = (file->offset + lastOffset) / tor->blockSize;
796    const uint64_t lastBlockOffset  = (file->offset + lastOffset) % tor->blockSize;
797    uint64_t haveBytes = 0;
798
799    assert( tor != NULL );
800    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
801    assert( file->offset + file->length <= tor->info.totalSize );
802    assert( ( (int)firstBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
803    assert( ( (int)lastBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
804    assert( firstBlock <= lastBlock );
805    assert( (int)tr_torBlockPiece( tor, firstBlock ) == file->firstPiece );
806    assert( (int)tr_torBlockPiece( tor, lastBlock ) == file->lastPiece );
807
808    if( firstBlock == lastBlock )
809    {
810        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
811            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
812    }
813    else
814    {
815        uint64_t i;
816
817        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
818            haveBytes += tor->blockSize - firstBlockOffset;
819
820        for( i=firstBlock+1; i<lastBlock; ++i )
821            if( tr_cpBlockIsComplete( tor->completion, i ) )
822               haveBytes += tor->blockSize;
823
824        if( tr_cpBlockIsComplete( tor->completion, lastBlock ) )
825            haveBytes += lastBlockOffset + 1;
826    }
827
828    return haveBytes;
829}
830
831tr_file_stat *
832tr_torrentFiles( const tr_torrent * tor, int * fileCount )
833{
834    int i;
835    const int n = tor->info.fileCount;
836    tr_file_stat * files = tr_new0( tr_file_stat, n );
837    tr_file_stat * walk = files;
838
839    for( i=0; i<n; ++i, ++walk )
840    {
841        const tr_file * file = tor->info.files + i;
842
843        walk->bytesCompleted = fileBytesCompleted( tor, i );
844
845        walk->progress = file->length
846            ? walk->bytesCompleted / (float)file->length
847            : 1.0;
848    }
849
850    *fileCount = n;
851
852    return files;
853}
854
855void
856tr_torrentFilesFree( tr_file_stat * files, int fileCount UNUSED )
857{
858    tr_free( files );
859}
860
861/***
862****
863***/
864
865tr_peer_stat *
866tr_torrentPeers( const tr_torrent * tor, int * peerCount )
867{
868    return tr_peerMgrPeerStats( tor->handle->peerMgr,
869                                tor->info.hash, peerCount );
870}
871
872void
873tr_torrentPeersFree( tr_peer_stat * peers, int peerCount )
874{
875    int i;
876    for( i=0; i<peerCount; ++i )
877        tr_free( (char*) peers[i].client );
878    tr_free( peers );
879}
880
881void tr_torrentAvailability( const tr_torrent * tor, int8_t * tab, int size )
882{
883    return tr_peerMgrTorrentAvailability( tor->handle->peerMgr,
884                                          tor->info.hash,
885                                          tab, size );
886}
887
888void tr_torrentAmountFinished( const tr_torrent * tor, float * tab, int size )
889{
890    int i;
891    float interval;
892    tr_torrentLock( tor );
893
894    interval = (float)tor->info.pieceCount / (float)size;
895    for( i = 0; i < size; i++ )
896    {
897        int piece = i * interval;
898        tab[i] = tr_cpPercentBlocksInPiece( tor->completion, piece );
899    }
900
901    tr_torrentUnlock( tor );
902}
903
904void
905tr_torrentResetTransferStats( tr_torrent * tor )
906{
907    tr_torrentLock( tor );
908
909    tor->downloadedPrev += tor->downloadedCur;
910    tor->downloadedCur   = 0;
911    tor->uploadedPrev   += tor->uploadedCur;
912    tor->uploadedCur     = 0;
913    tor->corruptPrev    += tor->corruptCur;
914    tor->corruptCur      = 0;
915
916    tr_torrentUnlock( tor );
917}
918
919
920void
921tr_torrentSetHasPiece( tr_torrent * tor, int pieceIndex, int has )
922{
923    tr_torrentLock( tor );
924
925    if( has )
926        tr_cpPieceAdd( tor->completion, pieceIndex );
927    else
928        tr_cpPieceRem( tor->completion, pieceIndex );
929
930    tr_torrentUnlock( tor );
931}
932
933void tr_torrentRemoveSaved( tr_torrent * tor )
934{
935    tr_metainfoRemoveSaved( tor->info.hashString, tor->handle->tag );
936}
937
938/***
939****
940***/
941
942static void
943freeTorrent( tr_torrent * tor )
944{
945    tr_torrent * t;
946    tr_handle * h = tor->handle;
947    tr_info * inf = &tor->info;
948
949    assert( tor != NULL );
950    assert( !tor->isRunning );
951
952    tr_globalLock( h );
953
954    tr_peerMgrRemoveTorrent( h->peerMgr, tor->info.hash );
955
956    tr_cpClose( tor->completion );
957
958    tr_rcClose( tor->upload );
959    tr_rcClose( tor->download );
960    tr_rcClose( tor->swarmspeed );
961
962    tr_trackerUnsubscribe( tor->tracker, tor->trackerSubscription );
963    tr_trackerFree( tor->tracker );
964    tor->tracker = NULL;
965
966    tr_free( tor->destination );
967
968    if( tor == h->torrentList )
969        h->torrentList = tor->next;
970    else for( t=h->torrentList; t!=NULL; t=t->next ) {
971        if( t->next == tor ) {
972            t->next = tor->next;
973            break;
974        }
975    }
976
977    assert( h->torrentCount >= 1 );
978    h->torrentCount--;
979
980    tr_inf( "closing torrent %s... %d torrents left",
981            tor->info.name, h->torrentCount );
982
983    tr_metainfoFree( inf );
984    tr_free( tor );
985
986    tr_globalUnlock( h );
987}
988
989enum
990{
991    AFTER_RECHECK_NONE,
992    AFTER_RECHECK_START,
993    AFTER_RECHECK_STOP,
994    AFTER_RECHECK_CLOSE
995};
996
997static void
998checkAndStartCB( tr_torrent * tor )
999{
1000    tr_globalLock( tor->handle );
1001
1002    tor->isRunning  = 1;
1003    *tor->errorString = '\0';
1004    tr_torrentResetTransferStats( tor );
1005    tr_torrentRecheckCompleteness( tor );
1006    tor->startDate = tr_date( );
1007    tr_trackerStart( tor->tracker );
1008    tr_peerMgrStartTorrent( tor->handle->peerMgr, tor->info.hash );
1009
1010    tr_globalUnlock( tor->handle );
1011}
1012   
1013void
1014tr_torrentStart( tr_torrent * tor )
1015{
1016    tr_globalLock( tor->handle );
1017
1018    if( !tor->isRunning )
1019    {
1020        tor->isRunning = 1;
1021        tr_ioRecheckAdd( tor, checkAndStartCB );
1022    }
1023
1024    tr_globalUnlock( tor->handle );
1025}
1026
1027void
1028tr_torrentRecheck( tr_torrent * tor )
1029{
1030    tr_globalLock( tor->handle );
1031
1032    if( !tor->uncheckedPieces )
1033        tor->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
1034    tr_bitfieldAddRange( tor->uncheckedPieces, 0, tor->info.pieceCount );
1035
1036    tr_ioRecheckAdd( tor, tr_torrentRecheckCompleteness );
1037
1038    tr_globalUnlock( tor->handle );
1039}
1040
1041
1042static void
1043stopTorrent( void * vtor )
1044{
1045    tr_torrent * tor = vtor;
1046    tr_ioRecheckRemove( tor );
1047    tr_peerMgrStopTorrent( tor->handle->peerMgr, tor->info.hash );
1048    tr_trackerStop( tor->tracker );
1049    tr_ioClose( tor );
1050}
1051
1052void
1053tr_torrentStop( tr_torrent * tor )
1054{
1055    tr_globalLock( tor->handle );
1056
1057    saveFastResumeNow( tor );
1058    tor->isRunning = 0;
1059    tr_runInEventThread( tor->handle, stopTorrent, tor );
1060
1061    tr_globalUnlock( tor->handle );
1062}
1063
1064static void
1065closeTorrent( void * vtor )
1066{
1067    tr_torrent * tor = vtor;
1068    saveFastResumeNow( tor );
1069    tor->isRunning = 0;
1070    stopTorrent( tor );
1071    freeTorrent( tor );
1072}
1073
1074void
1075tr_torrentClose( tr_torrent * tor )
1076{
1077    tr_globalLock( tor->handle );
1078
1079    tr_runInEventThread( tor->handle, closeTorrent, tor );
1080
1081    tr_globalUnlock( tor->handle );
1082}
1083
1084/**
1085***  Completeness
1086**/
1087
1088static void
1089fireStatusChange( tr_torrent * tor, cp_status_t status )
1090{
1091    assert( tor != NULL );
1092    assert( status==TR_CP_INCOMPLETE || status==TR_CP_DONE || status==TR_CP_COMPLETE );
1093
1094    if( tor->status_func != NULL )
1095        (tor->status_func)( tor, status, tor->status_func_user_data );
1096}
1097
1098void
1099tr_torrentSetStatusCallback( tr_torrent             * tor,
1100                             tr_torrent_status_func   func,
1101                             void                   * user_data )
1102{
1103    assert( tor != NULL );
1104    tor->status_func = func;
1105    tor->status_func_user_data = user_data;
1106}
1107
1108void
1109tr_torrentClearStatusCallback( tr_torrent * torrent )
1110{
1111    tr_torrentSetStatusCallback( torrent, NULL, NULL );
1112}
1113
1114void
1115tr_torrentRecheckCompleteness( tr_torrent * tor )
1116{
1117    cp_status_t cpStatus;
1118
1119    tr_torrentLock( tor );
1120
1121    cpStatus = tr_cpGetStatus( tor->completion );
1122    if( cpStatus != tor->cpStatus ) {
1123        tor->cpStatus = cpStatus;
1124        fireStatusChange( tor, cpStatus );
1125        if( (cpStatus == TR_CP_COMPLETE) /* ...and if we're complete */
1126            && tor->downloadedCur ) {        /* and it just happened */
1127            tr_trackerCompleted( tor->tracker ); /* tell the tracker */
1128        }
1129        tr_ioClose( tor );
1130        saveFastResumeSoon( tor );
1131    }
1132    tr_torrentUnlock( tor );
1133}
1134
1135/**
1136***  File priorities
1137**/
1138
1139void
1140tr_torrentSetFilePriority( tr_torrent   * tor,
1141                           int            fileIndex,
1142                           tr_priority_t  priority )
1143{
1144    int i;
1145    tr_file * file;
1146
1147    tr_torrentLock( tor );
1148
1149    assert( tor != NULL );
1150    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
1151    assert( priority==TR_PRI_LOW || priority==TR_PRI_NORMAL || priority==TR_PRI_HIGH );
1152
1153    file = &tor->info.files[fileIndex];
1154    file->priority = priority;
1155    for( i=file->firstPiece; i<=file->lastPiece; ++i )
1156      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
1157
1158    tr_dbg ( "Setting file #%d (pieces %d-%d) priority to %d (%s)",
1159             fileIndex, file->firstPiece, file->lastPiece,
1160             priority, tor->info.files[fileIndex].name );
1161
1162    saveFastResumeSoon( tor );
1163
1164    tr_torrentUnlock( tor );
1165}
1166
1167void
1168tr_torrentSetFilePriorities( tr_torrent     * tor,
1169                             int            * files,
1170                             int              fileCount,
1171                             tr_priority_t    priority )
1172{
1173    int i;
1174    for( i=0; i<fileCount; ++i )
1175        tr_torrentSetFilePriority( tor, files[i], priority );
1176}
1177
1178tr_priority_t
1179tr_torrentGetFilePriority( const tr_torrent *  tor, int file )
1180{
1181    tr_priority_t ret;
1182
1183    tr_torrentLock( tor );
1184    assert( tor != NULL );
1185    assert( 0<=file && file<tor->info.fileCount );
1186    ret = tor->info.files[file].priority;
1187    tr_torrentUnlock( tor );
1188
1189    return ret;
1190}
1191
1192tr_priority_t*
1193tr_torrentGetFilePriorities( const tr_torrent * tor )
1194{
1195    int i;
1196    tr_priority_t * p;
1197
1198    tr_torrentLock( tor );
1199    p = tr_new0( tr_priority_t, tor->info.fileCount );
1200    for( i=0; i<tor->info.fileCount; ++i )
1201        p[i] = tor->info.files[i].priority;
1202    tr_torrentUnlock( tor );
1203
1204    return p;
1205}
1206
1207/**
1208***  File DND
1209**/
1210
1211int
1212tr_torrentGetFileDL( const tr_torrent * tor,
1213                     int                file )
1214{
1215    int doDownload;
1216    tr_torrentLock( tor );
1217
1218    assert( 0<=file && file<tor->info.fileCount );
1219    doDownload = !tor->info.files[file].dnd;
1220
1221    tr_torrentUnlock( tor );
1222    return doDownload != 0;
1223}
1224
1225void
1226tr_torrentSetFileDL( tr_torrent  * tor,
1227                     int           fileIndex,
1228                     int           doDownload )
1229{
1230    tr_file * file;
1231    const int dnd = !doDownload;
1232    int firstPiece, firstPieceDND;
1233    int lastPiece, lastPieceDND;
1234    int i;
1235
1236    tr_torrentLock( tor );
1237
1238    file = &tor->info.files[fileIndex];
1239    file->dnd = dnd;
1240    firstPiece = file->firstPiece;
1241    lastPiece = file->lastPiece;
1242
1243    /* can't set the first piece to DND unless
1244       every file using that piece is DND */
1245    firstPieceDND = dnd;
1246    for( i=fileIndex-1; firstPieceDND && i>=0; --i ) {
1247        if( tor->info.files[i].lastPiece != firstPiece )
1248            break;
1249        firstPieceDND = tor->info.files[i].dnd;
1250    }
1251
1252    /* can't set the last piece to DND unless
1253       every file using that piece is DND */
1254    lastPieceDND = dnd;
1255    for( i=fileIndex+1; lastPieceDND && i<tor->info.fileCount; ++i ) {
1256        if( tor->info.files[i].firstPiece != lastPiece )
1257            break;
1258        lastPieceDND = tor->info.files[i].dnd;
1259    }
1260
1261    if( firstPiece == lastPiece )
1262    {
1263        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
1264    }
1265    else
1266    {
1267        tor->info.pieces[firstPiece].dnd = firstPieceDND;
1268        tor->info.pieces[lastPiece].dnd = lastPieceDND;
1269        for( i=firstPiece+1; i<lastPiece; ++i )
1270            tor->info.pieces[i].dnd = dnd;
1271    }
1272
1273    tr_cpInvalidateDND ( tor->completion );
1274
1275    saveFastResumeSoon( tor );
1276
1277    tr_torrentUnlock( tor );
1278}
1279
1280void
1281tr_torrentSetFileDLs ( tr_torrent  * tor,
1282                       int         * files,
1283                       int           fileCount,
1284                       int           doDownload )
1285{
1286    int i;
1287    for( i=0; i<fileCount; ++i )
1288        tr_torrentSetFileDL( tor, files[i], doDownload );
1289}
1290
1291/***
1292****
1293***/
1294
1295int _tr_block( const tr_torrent * tor, int index, int begin )
1296{
1297    const tr_info * inf = &tor->info;
1298    return index * ( inf->pieceSize / tor->blockSize ) +
1299        begin / tor->blockSize;
1300}
1301
1302uint64_t
1303tr_pieceOffset( const tr_torrent * tor, int index, int begin, int length )
1304{
1305    uint64_t ret;
1306    ret = tor->info.pieceSize;
1307    ret *= index;
1308    ret += begin;
1309    ret += length;
1310    return ret;
1311}
1312
Note: See TracBrowser for help on using the repository browser.