source: trunk/libtransmission/torrent.c @ 3127

Last change on this file since 3127 was 3127, checked in by charles, 15 years ago
  • remove some console messages.
  • scrape sooner when peers are added.
  • added stub code for the connection manager code
  • Property svn:keywords set to Date Rev Author Id
File size: 32.7 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 3127 2007-09-22 00:22:10Z 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_lockLock ( (tr_lock*)tor->lock );
94}
95
96void
97tr_torrentUnlock( const tr_torrent * tor )
98{
99    tr_lockUnlock ( (tr_lock*)tor->lock );
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 = (tr_torrent *) user_data;
150    tr_tracker_event_t * event = (tr_tracker_event_t *) 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_STOPPED:
175            //assert( tor->runStatus == TR_RUN_STOPPING );
176            tor->runStatus = TR_RUN_STOPPED;
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 recheckCpState( tr_torrent * );
257
258static void
259tr_torrentStartImpl( tr_torrent * tor )
260{
261    assert( tor != NULL );
262    assert( tor->runStatus == TR_RUN_RUNNING );
263
264    *tor->errorString = '\0';
265    tr_torrentResetTransferStats( tor );
266    recheckCpState( tor );
267    tor->startDate = tr_date();
268    tr_trackerStart( tor->tracker );
269    tr_peerMgrStartTorrent( tor->handle->peerMgr, tor->info.hash );
270}
271
272static void
273recheckDoneCB( tr_torrent * tor )
274{
275    recheckCpState( tor );
276
277    if( tor->doStopAfterHashCheck ) {
278        tor->doStopAfterHashCheck = 0;
279        tr_torrentStop( tor );
280    }
281
282    if( tor->runStatus == TR_RUN_RUNNING )
283        tr_torrentStartImpl( tor );
284}
285
286static void
287torrentRealInit( tr_handle  * h,
288                 tr_torrent * tor,
289                 const char * destination,
290                 int          flags )
291{
292    uint64_t loaded;
293    uint64_t t;
294    tr_bitfield * uncheckedPieces;
295    tr_info * info = &tor->info;
296   
297    tor->info.flags |= flags;
298
299    tr_sharedLock( h->shared );
300
301    tor->destination = tr_strdup( destination );
302
303    tor->handle   = h;
304    tor->azId     = h->azId;
305    tor->hasChangedState = -1;
306    tor->pexDisabled = 0;
307
308    tor->runStatusToSaveIsSet = FALSE;
309
310    /**
311     * Decide on a block size.  constraints:
312     * (1) most clients decline requests over 16 KiB
313     * (2) pieceSize must be a multiple of block size
314     */
315    tor->blockSize = info->pieceSize;
316    while( tor->blockSize > (1024*16) )
317        tor->blockSize /= 2;
318
319    tor->lastPieceSize = info->totalSize % info->pieceSize;
320
321    if( !tor->lastPieceSize )
322         tor->lastPieceSize = info->pieceSize;
323
324    tor->lastBlockSize = info->totalSize % tor->blockSize;
325
326    if( !tor->lastBlockSize )
327         tor->lastBlockSize = tor->blockSize;
328
329    tor->blockCount =
330        ( info->totalSize + tor->blockSize - 1 ) / tor->blockSize;
331
332    tor->blockCountInPiece =
333        info->pieceSize / tor->blockSize;
334
335    tor->blockCountInLastPiece =
336        ( tor->lastPieceSize + tor->blockSize - 1 ) / tor->blockSize;
337
338    /* check our work */
339    assert( ( info->pieceSize % tor->blockSize ) == 0 );
340    t = info->pieceCount - 1;
341    t *= info->pieceSize;
342    t += tor->lastPieceSize;
343    assert( t == info->totalSize );
344    t = tor->blockCount - 1;
345    t *= tor->blockSize;
346    t += tor->lastBlockSize;
347    assert( t == info->totalSize );
348    t = info->pieceCount - 1;
349    t *= tor->blockCountInPiece;
350    t += tor->blockCountInLastPiece;
351    assert( t == (uint64_t)tor->blockCount );
352
353    tor->completion = tr_cpInit( tor );
354
355    tr_torrentInitFilePieces( tor );
356
357    tor->lock = tr_lockNew( );
358
359    tor->upload         = tr_rcInit();
360    tor->download       = tr_rcInit();
361    tor->swarmspeed     = tr_rcInit();
362
363    tr_sha1( tor->obfuscatedHash, "req2", 4,
364                                  info->hash, SHA_DIGEST_LENGTH,
365                                  NULL );
366 
367    tr_sharedUnlock( h->shared );
368
369    tr_peerMgrAddTorrent( h->peerMgr, tor );
370
371    if( !h->isPortSet )
372        tr_setBindPort( h, TR_DEFAULT_PORT );
373
374    assert( !tor->downloadedCur );
375    assert( !tor->uploadedCur );
376
377    tor->error   = TR_OK;
378
379    uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
380    loaded = tr_fastResumeLoad( tor, uncheckedPieces );
381
382    /* the `paused' flag has highest precedence...
383       after that, the fastresume setting is used...
384       if that's not found, default to RUNNING */
385    if( flags & TR_FLAG_PAUSED )
386        tor->runStatus = TR_RUN_STOPPED;
387    else if( !(loaded & TR_FR_RUN ) )
388        tor->runStatus = TR_RUN_RUNNING;
389
390    if( tr_bitfieldIsEmpty( uncheckedPieces ) )
391        tr_bitfieldFree( uncheckedPieces );
392    else
393        tor->uncheckedPieces = uncheckedPieces;
394
395
396    if( !(loaded & TR_FR_SPEEDLIMIT ) ) {
397        int limit, enabled;
398        tr_getGlobalSpeedLimit( tor->handle, TR_UP, &enabled, &limit );
399        tr_torrentSetSpeedLimit( tor, TR_UP, limit );
400        tr_getGlobalSpeedLimit( tor->handle, TR_DOWN, &enabled, &limit );
401        tr_torrentSetSpeedLimit( tor, TR_DOWN, limit );
402    }
403
404    tor->cpStatus = tr_cpGetStatus( tor->completion );
405
406    tor->tracker = tr_trackerNew( tor );
407    tor->trackerSubscription = tr_trackerSubscribe( tor->tracker, onTrackerResponse, tor );
408
409    tr_sharedLock( h->shared );
410    tor->next = h->torrentList;
411    h->torrentList = tor;
412    h->torrentCount++;
413    tr_sharedUnlock( h->shared );
414
415    tr_ioRecheckAdd( tor, recheckDoneCB, tor->runStatus );
416}
417
418static int
419pathIsInUse ( const tr_handle   * h,
420              const char        * destination,
421              const char        * name )
422{
423    const tr_torrent * tor;
424   
425    for( tor=h->torrentList; tor; tor=tor->next )
426        if( !strcmp( destination, tor->destination )
427         && !strcmp( name, tor->info.name ) )
428            return TRUE;
429
430    return FALSE;
431}
432
433static int
434hashExists( const tr_handle   * h,
435            const uint8_t     * hash )
436{
437    const tr_torrent * tor;
438
439    for( tor=h->torrentList; tor; tor=tor->next )
440        if( !memcmp( hash, tor->info.hash, SHA_DIGEST_LENGTH ) )
441            return TRUE;
442
443    return FALSE;
444}
445
446static int
447infoCanAdd( const tr_handle   * h,
448            const char        * destination,
449            const tr_info     * info )
450{
451    if( hashExists( h, info->hash ) )
452        return TR_EDUPLICATE;
453
454    if( destination && pathIsInUse( h, destination, info->name ) )
455        return TR_EDUPLICATE;
456
457    return TR_OK;
458}
459
460int
461tr_torrentParse( const tr_handle  * h,
462                 const char       * path,
463                 const char       * destination,
464                 tr_info          * setme_info )
465{
466    int ret, doFree;
467    tr_info tmp;
468
469    if( setme_info == NULL )
470        setme_info = &tmp;
471
472    memset( setme_info, 0, sizeof( tr_info ) );
473    ret = tr_metainfoParseFile( setme_info, h->tag, path, FALSE );
474    doFree = !ret && (setme_info == &tmp);
475
476    if( ret == TR_OK )
477        ret = infoCanAdd( h, destination, setme_info );
478
479    if( doFree )
480        tr_metainfoFree( &tmp );
481
482    return ret;
483}
484 
485tr_torrent *
486tr_torrentInit( tr_handle   * h,
487                const char  * path,
488                const char  * destination,
489                int           flags,
490                int         * error )
491{
492    int val;
493    int tmpError;
494    tr_torrent * tor = NULL;
495
496    if( !error )
497         error = &tmpError;
498
499    if(( val = tr_torrentParse( h, path, destination, NULL )))
500        *error = val;
501    else if(!(( tor = tr_new0( tr_torrent, 1 ))))
502        *error = TR_EOTHER;
503    else {
504        tr_metainfoParseFile( &tor->info, h->tag, path, TR_FLAG_SAVE & flags );
505        torrentRealInit( h, tor, destination, flags );
506    }
507
508    return tor;
509}
510
511int
512tr_torrentParseHash( const tr_handle  * h,
513                     const char       * hashStr,
514                     const char       * destination,
515                     tr_info          * setme_info )
516{
517    int ret, doFree;
518    tr_info tmp;
519
520    if( setme_info == NULL )
521        setme_info = &tmp;
522
523    memset( setme_info, 0, sizeof( tr_info ) );
524    ret = tr_metainfoParseHash( setme_info, h->tag, hashStr );
525    doFree = !ret && (setme_info == &tmp);
526
527    if( ret == TR_OK )
528        ret = infoCanAdd( h, destination, setme_info );
529
530    if( doFree )
531        tr_metainfoFree( &tmp );
532
533    return ret;
534}
535
536tr_torrent *
537tr_torrentInitSaved( tr_handle    * h,
538                     const char   * hashStr,
539                     const char   * destination,
540                     int            flags,
541                     int          * error )
542{
543    int val;
544    int tmpError;
545    tr_torrent * tor = NULL;
546
547    if( !error )
548         error = &tmpError;
549
550    if(( val = tr_torrentParseHash( h, hashStr, destination, NULL )))
551        *error = val;
552    else if(!(( tor = tr_new0( tr_torrent, 1 ))))
553        *error = TR_EOTHER;
554    else {
555        tr_metainfoParseHash( &tor->info, h->tag, hashStr );
556        torrentRealInit( h, tor, destination, (TR_FLAG_SAVE|flags) );
557    }
558
559    return tor;
560}
561
562static int
563tr_torrentParseData( const tr_handle  * h,
564                     const uint8_t    * data,
565                     size_t             size,
566                     const char       * destination,
567                     tr_info          * setme_info )
568{
569    int ret, doFree;
570    tr_info tmp;
571
572    if( setme_info == NULL )
573        setme_info = &tmp;
574
575    memset( setme_info, 0, sizeof( tr_info ) );
576    ret = tr_metainfoParseData( setme_info, h->tag, data, size, FALSE );
577    doFree = !ret && (setme_info == &tmp);
578
579    if( ret == TR_OK )
580        ret = infoCanAdd( h, destination, setme_info );
581
582    if( doFree )
583        tr_metainfoFree( &tmp );
584
585    return ret;
586}
587
588tr_torrent *
589tr_torrentInitData( tr_handle      * h,
590                    const uint8_t  * data,
591                    size_t           size,
592                    const char     * destination,
593                    int              flags,
594                    int            * error )
595{
596    int val;
597    int tmpError;
598    tr_torrent * tor = NULL;
599
600    if( !error )
601         error = &tmpError;
602
603    if(( val = tr_torrentParseData( h, data, size, destination, NULL )))
604        *error = val;
605    else if(!(( tor = tr_new0( tr_torrent, 1 ))))
606        *error = TR_EOTHER;
607    else {
608        tr_metainfoParseData( &tor->info, h->tag, data, size, TR_FLAG_SAVE & flags );
609        torrentRealInit( h, tor, destination, flags );
610    }
611
612    return tor;
613}
614
615const tr_info *
616tr_torrentInfo( const tr_torrent * tor )
617{
618    return &tor->info;
619}
620
621/***
622****
623***/
624
625#if 0
626int tr_torrentScrape( tr_torrent * tor, int * s, int * l, int * d )
627{
628    return tr_trackerScrape( tor, s, l, d );
629}
630#endif
631
632static int
633saveFastResumeNow( void * vtor )
634{
635    tr_torrent * tor = (tr_torrent *) vtor;
636
637    tr_fastResumeSave( tor );
638    recheckCpState( tor );
639
640    tr_timerFree( &tor->saveTimer );
641    return FALSE;
642}
643
644static void
645saveFastResumeSoon( void * vtor )
646{
647    tr_torrent * tor = (tr_torrent *) vtor;
648
649    if( tor->saveTimer == NULL )
650        tor->saveTimer = tr_timerNew( tor->handle, saveFastResumeNow, tor, 1000 );
651}
652
653/**
654***
655**/
656
657void
658tr_torrentSetFolder( tr_torrent * tor, const char * path )
659{
660    tr_free( tor->destination );
661    tor->destination = tr_strdup( path );
662    saveFastResumeSoon( tor );
663}
664
665const char*
666tr_torrentGetFolder( const tr_torrent * tor )
667{
668    return tor->destination;
669}
670
671void
672tr_torrentChangeMyPort( tr_torrent * tor )
673{
674    if( tor->tracker )
675        tr_trackerChangeMyPort( tor->tracker );
676}
677
678
679void
680tr_torrentDisablePex( tr_torrent * tor, int disable )
681{
682    assert( tor != NULL );
683    assert( disable==0 || disable==1 );
684    //assert( tor->runStatus != TR_RUN_RUNNING );
685
686    /* pex is ALWAYS disabled for private torrents */
687    if( tor->info.flags & TR_FLAG_PRIVATE )
688        disable = TRUE;
689
690    tor->pexDisabled = disable;
691}
692
693static int tr_didStateChangeTo ( tr_torrent * tor, int status )
694{
695    int ret;
696
697    tr_torrentLock( tor );
698    if (( ret = tor->hasChangedState == status ))
699        tor->hasChangedState = -1;
700    tr_torrentUnlock( tor );
701
702    return ret;
703}
704
705int tr_getIncomplete( tr_torrent * tor )
706{
707    return tr_didStateChangeTo( tor, TR_CP_INCOMPLETE );
708}
709int tr_getDone( tr_torrent * tor )
710{
711    return tr_didStateChangeTo( tor, TR_CP_DONE );
712}
713int tr_getComplete( tr_torrent * tor )
714{
715    return tr_didStateChangeTo( tor, TR_CP_COMPLETE );
716}
717
718void
719tr_manualUpdate( tr_torrent * tor )
720{
721    if( tor->runStatus == TR_RUN_RUNNING )
722    tr_trackerReannounce( tor->tracker );
723}
724int
725tr_torrentCanManualUpdate( const tr_torrent * tor )
726{
727    return ( tor != NULL )
728        && ( tor->runStatus == TR_RUN_RUNNING )
729        && ( tr_trackerCanManualAnnounce( tor->tracker ) );
730}
731
732const tr_stat *
733tr_torrentStat( tr_torrent * tor )
734{
735    tr_stat * s;
736    struct tr_tracker * tc;
737
738    tr_torrentLock( tor );
739
740    tor->statCur = !tor->statCur;
741    s = &tor->stats[tor->statCur];
742
743    s->error  = tor->error;
744    memcpy( s->errorString, tor->errorString,
745            sizeof( s->errorString ) );
746
747    tc = tor->tracker;
748    s->tracker = tr_trackerGetAddress( tor->tracker );
749
750    tr_peerMgrTorrentStats( tor->handle->peerMgr,
751                            tor->info.hash,
752                            &s->peersTotal,
753                            &s->peersConnected,
754                            &s->peersSendingToUs,
755                            &s->peersGettingFromUs,
756                             s->peersFrom );
757
758    s->percentComplete = tr_cpPercentComplete ( tor->completion );
759
760    s->percentDone = tr_cpPercentDone( tor->completion );
761    s->left = tr_cpLeftUntilDone( tor->completion );
762
763    switch( tor->runStatus ) {
764        case TR_RUN_CHECKING_WAIT: s->status = TR_STATUS_CHECK_WAIT; break;
765        case TR_RUN_CHECKING: s->status = TR_STATUS_CHECK; break;
766        case TR_RUN_STOPPING: s->status = TR_STATUS_STOPPING; break;
767        case TR_RUN_STOPPED: s->status = TR_STATUS_STOPPED; break;
768        case TR_RUN_RUNNING: switch( tor->cpStatus ) {
769            case TR_CP_INCOMPLETE: s->status = TR_STATUS_DOWNLOAD; break;
770            case TR_CP_DONE: s->status = TR_STATUS_DONE; break;
771            case TR_CP_COMPLETE: s->status = TR_STATUS_SEED; break;
772        }
773    }
774
775    s->recheckProgress = (tor->uncheckedPieces == NULL)
776        ? 0.0
777        : 1.0 - ((double)tr_bitfieldCountTrueBits(tor->uncheckedPieces) / tor->info.pieceCount);
778
779    s->cpStatus = tor->cpStatus;
780
781    /* tr_rcRate() doesn't make the difference between 'piece'
782       messages and other messages, which causes a non-zero
783       download rate even tough we are not downloading. So we
784       force it to zero not to confuse the user. */
785    s->rateDownload = tor->runStatus==TR_RUN_RUNNING
786        ? tr_rcRate( tor->download )
787        : 0.0;
788    s->rateUpload = tr_rcRate( tor->upload );
789   
790    tr_trackerGetCounts( tc,
791                         &s->completedFromTracker,
792                         &s->leechers, 
793                         &s->seeders );
794
795    s->swarmspeed = tr_rcRate( tor->swarmspeed );
796   
797    s->startDate = tor->startDate;
798    s->activityDate = tor->activityDate;
799
800    s->eta = s->rateDownload < 0.1
801        ? -1.0f
802        : (s->left / s->rateDownload / 1024.0);
803
804    s->corrupt         = tor->corruptCur    + tor->corruptPrev;
805    s->uploaded        = tor->uploadedCur   + tor->uploadedPrev;
806    s->downloaded      = tor->downloadedCur + tor->downloadedPrev;
807    s->downloadedValid = tr_cpDownloadedValid( tor->completion );
808   
809    s->ratio = s->downloaded || s->downloadedValid
810      ? (float)s->uploaded / (float)MAX(s->downloaded, s->downloadedValid)
811      : TR_RATIO_NA; 
812   
813    tr_torrentUnlock( tor );
814
815    return s;
816}
817
818/***
819****
820***/
821
822static uint64_t
823fileBytesCompleted ( const tr_torrent * tor, int fileIndex )
824{
825    const tr_file * file     =  &tor->info.files[fileIndex];
826    const uint64_t firstBlock       =  file->offset / tor->blockSize;
827    const uint64_t firstBlockOffset =  file->offset % tor->blockSize;
828    const uint64_t lastOffset       =  file->length ? (file->length-1) : 0;
829    const uint64_t lastBlock        = (file->offset + lastOffset) / tor->blockSize;
830    const uint64_t lastBlockOffset  = (file->offset + lastOffset) % tor->blockSize;
831    uint64_t haveBytes = 0;
832
833    assert( tor != NULL );
834    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
835    assert( file->offset + file->length <= tor->info.totalSize );
836    assert( ( (int)firstBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
837    assert( ( (int)lastBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
838    assert( firstBlock <= lastBlock );
839    assert( (int)tr_torBlockPiece( tor, firstBlock ) == file->firstPiece );
840    assert( (int)tr_torBlockPiece( tor, lastBlock ) == file->lastPiece );
841
842    if( firstBlock == lastBlock )
843    {
844        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
845            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
846    }
847    else
848    {
849        uint64_t i;
850
851        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
852            haveBytes += tor->blockSize - firstBlockOffset;
853
854        for( i=firstBlock+1; i<lastBlock; ++i )
855            if( tr_cpBlockIsComplete( tor->completion, i ) )
856               haveBytes += tor->blockSize;
857
858        if( tr_cpBlockIsComplete( tor->completion, lastBlock ) )
859            haveBytes += lastBlockOffset + 1;
860    }
861
862    return haveBytes;
863}
864
865tr_file_stat *
866tr_torrentFiles( const tr_torrent * tor, int * fileCount )
867{
868    int i;
869    const int n = tor->info.fileCount;
870    tr_file_stat * files = tr_new0( tr_file_stat, n );
871    tr_file_stat * walk = files;
872
873    for( i=0; i<n; ++i, ++walk )
874    {
875        const tr_file * file = tor->info.files + i;
876        cp_status_t cp;
877
878        walk->bytesCompleted = fileBytesCompleted( tor, i );
879
880        walk->progress = file->length
881            ? walk->bytesCompleted / (float)file->length
882            : 1.0;
883
884        if( walk->bytesCompleted >= file->length )
885            cp = TR_CP_COMPLETE;
886        else if( tor->info.files[i].dnd )
887            cp = TR_CP_DONE;
888        else
889            cp = TR_CP_INCOMPLETE;
890
891        walk->completionStatus = cp;
892    }
893
894    *fileCount = n;
895
896    return files;
897}
898
899void
900tr_torrentFilesFree( tr_file_stat * files, int fileCount UNUSED )
901{
902    tr_free( files );
903}
904
905/***
906****
907***/
908
909tr_peer_stat *
910tr_torrentPeers( const tr_torrent * tor, int * peerCount )
911{
912    return tr_peerMgrPeerStats( tor->handle->peerMgr,
913                                tor->info.hash, peerCount );
914}
915
916void tr_torrentPeersFree( tr_peer_stat * peers, int peerCount UNUSED )
917{
918    tr_free( peers );
919}
920
921void tr_torrentAvailability( const tr_torrent * tor, int8_t * tab, int size )
922{
923    return tr_peerMgrTorrentAvailability( tor->handle->peerMgr,
924                                          tor->info.hash,
925                                          tab, size );
926}
927
928void tr_torrentAmountFinished( const tr_torrent * tor, float * tab, int size )
929{
930    int i;
931    float interval;
932    tr_torrentLock( tor );
933
934    interval = (float)tor->info.pieceCount / (float)size;
935    for( i = 0; i < size; i++ )
936    {
937        int piece = i * interval;
938        tab[i] = tr_cpPercentBlocksInPiece( tor->completion, piece );
939    }
940
941    tr_torrentUnlock( tor );
942}
943
944void
945tr_torrentResetTransferStats( tr_torrent * tor )
946{
947    tr_torrentLock( tor );
948
949    tor->downloadedPrev += tor->downloadedCur;
950    tor->downloadedCur   = 0;
951    tor->uploadedPrev   += tor->uploadedCur;
952    tor->uploadedCur     = 0;
953    tor->corruptPrev    += tor->corruptCur;
954    tor->corruptCur      = 0;
955
956    tr_torrentUnlock( tor );
957}
958
959
960void
961tr_torrentSetHasPiece( tr_torrent * tor, int pieceIndex, int has )
962{
963    tr_torrentLock( tor );
964
965    if( has )
966        tr_cpPieceAdd( tor->completion, pieceIndex );
967    else
968        tr_cpPieceRem( tor->completion, pieceIndex );
969
970    tr_torrentUnlock( tor );
971}
972
973void tr_torrentRemoveSaved( tr_torrent * tor )
974{
975    tr_metainfoRemoveSaved( tor->info.hashString, tor->handle->tag );
976}
977
978void tr_torrentRecheck( tr_torrent * tor )
979{
980    if( !tor->uncheckedPieces )
981        tor->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
982    tr_bitfieldAddRange( tor->uncheckedPieces, 0, tor->info.pieceCount );
983
984    tr_ioRecheckAdd( tor, recheckDoneCB, tor->runStatus );
985}
986
987/***
988****
989***/
990
991void
992tr_torrentStart( tr_torrent * tor )
993{
994    tr_ioRecheckAdd( tor, recheckDoneCB, TR_RUN_RUNNING );
995}
996
997static void
998tr_torrentFree( tr_torrent * tor )
999{
1000    tr_torrent * t;
1001    tr_handle * h = tor->handle;
1002    tr_info * inf = &tor->info;
1003
1004    assert( tor != NULL );
1005    assert( tor->runStatus == TR_RUN_STOPPED );
1006
1007    tr_sharedLock( h->shared );
1008
1009    tr_lockFree( tor->lock );
1010    tr_cpClose( tor->completion );
1011
1012    tr_rcClose( tor->upload );
1013    tr_rcClose( tor->download );
1014    tr_rcClose( tor->swarmspeed );
1015
1016    tr_trackerUnsubscribe( tor->tracker, tor->trackerSubscription );
1017    tr_trackerFree( tor->tracker );
1018    tor->tracker = NULL;
1019
1020    tr_free( tor->destination );
1021
1022    if( tor == h->torrentList )
1023        h->torrentList = tor->next;
1024    else for( t=h->torrentList; t!=NULL; t=t->next ) {
1025        if( t->next == tor ) {
1026            t->next = tor->next;
1027            break;
1028        }
1029    }
1030
1031    assert( h->torrentCount >= 1 );
1032    h->torrentCount--;
1033
1034    tr_inf( "closing torrent %s... %d torrents left",
1035            tor->info.name, h->torrentCount );
1036
1037    tr_peerMgrRemoveTorrent( h->peerMgr, tor->info.hash );
1038
1039    tr_metainfoFree( inf );
1040    tr_free( tor );
1041
1042    tr_sharedUnlock( h->shared );
1043}
1044
1045static int
1046freeWhenStopped( void * vtor )
1047{
1048    tr_torrent * tor = vtor;
1049
1050    if( tor->runStatus != TR_RUN_STOPPED ) /* keep waiting */
1051        return TRUE;
1052
1053    tr_torrentFree( tor );
1054    return FALSE;
1055}
1056
1057static void
1058tr_torrentStopImpl( void * vtor )
1059{
1060    tr_torrent * tor = vtor;
1061
1062    switch( tor->runStatus )
1063    {
1064        case TR_RUN_CHECKING_WAIT:
1065        case TR_RUN_CHECKING:
1066            tor->doStopAfterHashCheck = 1;
1067            tr_ioRecheckRemove( tor );
1068            break;
1069
1070        case TR_RUN_RUNNING:
1071            saveFastResumeNow( tor );
1072            tr_peerMgrStopTorrent( tor->handle->peerMgr, tor->info.hash );
1073            tor->runStatus = TR_RUN_STOPPING;
1074            tr_trackerStop( tor->tracker );
1075            tr_ioClose( tor );
1076            break;
1077
1078        case TR_RUN_STOPPING:
1079        case TR_RUN_STOPPED:
1080            break;
1081    }
1082}
1083
1084void
1085tr_torrentStop( tr_torrent * tor )
1086{
1087    tr_runInEventThread( tor->handle, tr_torrentStopImpl, tor );
1088}
1089
1090void
1091tr_torrentClose( tr_torrent * tor )
1092{
1093    tor->runStatusToSave = tor->runStatus;
1094    tor->runStatusToSaveIsSet = TRUE;
1095    tr_torrentStop( tor );
1096    tr_timerNew( tor->handle, freeWhenStopped, tor, 250 );
1097}
1098
1099static void
1100recheckCpState( tr_torrent * tor )
1101{
1102    cp_status_t cpStatus;
1103
1104    tr_torrentLock( tor );
1105
1106    cpStatus = tr_cpGetStatus( tor->completion );
1107    if( cpStatus != tor->cpStatus ) {
1108        tor->cpStatus = cpStatus;
1109        tor->hasChangedState = tor->cpStatus;  /* tell the client... */
1110        if( (cpStatus == TR_CP_COMPLETE) /* ...and if we're complete */
1111            && tor->downloadedCur ) {        /* and it just happened */
1112            tr_trackerCompleted( tor->tracker ); /* tell the tracker */
1113        }
1114        tr_ioClose( tor );
1115        saveFastResumeSoon( tor );
1116    }
1117    tr_torrentUnlock( tor );
1118}
1119
1120/**
1121***  File priorities
1122**/
1123
1124void
1125tr_torrentSetFilePriority( tr_torrent   * tor,
1126                           int            fileIndex,
1127                           tr_priority_t  priority )
1128{
1129    int i;
1130    tr_file * file;
1131
1132    tr_torrentLock( tor );
1133
1134    assert( tor != NULL );
1135    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
1136    assert( priority==TR_PRI_LOW || priority==TR_PRI_NORMAL || priority==TR_PRI_HIGH );
1137
1138    file = &tor->info.files[fileIndex];
1139    file->priority = priority;
1140    for( i=file->firstPiece; i<=file->lastPiece; ++i )
1141      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
1142
1143    tr_dbg ( "Setting file #%d (pieces %d-%d) priority to %d (%s)",
1144             fileIndex, file->firstPiece, file->lastPiece,
1145             priority, tor->info.files[fileIndex].name );
1146
1147    saveFastResumeSoon( tor );
1148
1149    tr_torrentUnlock( tor );
1150}
1151
1152void
1153tr_torrentSetFilePriorities( tr_torrent     * tor,
1154                             int            * files,
1155                             int              fileCount,
1156                             tr_priority_t    priority )
1157{
1158    int i;
1159    for( i=0; i<fileCount; ++i )
1160        tr_torrentSetFilePriority( tor, files[i], priority );
1161}
1162
1163tr_priority_t
1164tr_torrentGetFilePriority( const tr_torrent *  tor, int file )
1165{
1166    tr_priority_t ret;
1167
1168    tr_torrentLock( tor );
1169    assert( tor != NULL );
1170    assert( 0<=file && file<tor->info.fileCount );
1171    ret = tor->info.files[file].priority;
1172    tr_torrentUnlock( tor );
1173
1174    return ret;
1175}
1176
1177
1178tr_priority_t*
1179tr_torrentGetFilePriorities( const tr_torrent * tor )
1180{
1181    int i;
1182    tr_priority_t * p;
1183
1184    tr_torrentLock( tor );
1185    p = tr_new0( tr_priority_t, tor->info.fileCount );
1186    for( i=0; i<tor->info.fileCount; ++i )
1187        p[i] = tor->info.files[i].priority;
1188    tr_torrentUnlock( tor );
1189
1190    return p;
1191}
1192
1193/**
1194***  File DND
1195**/
1196
1197int
1198tr_torrentGetFileDL( const tr_torrent * tor,
1199                     int                file )
1200{
1201    int doDownload;
1202    tr_torrentLock( tor );
1203
1204    assert( 0<=file && file<tor->info.fileCount );
1205    doDownload = !tor->info.files[file].dnd;
1206
1207    tr_torrentUnlock( tor );
1208    return doDownload != 0;
1209}
1210
1211void
1212tr_torrentSetFileDL( tr_torrent  * tor,
1213                     int           fileIndex,
1214                     int           doDownload )
1215{
1216    tr_file * file;
1217    const int dnd = !doDownload;
1218    int firstPiece, firstPieceDND;
1219    int lastPiece, lastPieceDND;
1220    int i;
1221
1222    tr_torrentLock( tor );
1223
1224    file = &tor->info.files[fileIndex];
1225    file->dnd = dnd;
1226    firstPiece = file->firstPiece;
1227    lastPiece = file->lastPiece;
1228
1229    /* can't set the first piece to DND unless
1230       every file using that piece is DND */
1231    firstPieceDND = dnd;
1232    for( i=fileIndex-1; firstPieceDND && i>=0; --i ) {
1233        if( tor->info.files[i].lastPiece != firstPiece )
1234            break;
1235        firstPieceDND = tor->info.files[i].dnd;
1236    }
1237
1238    /* can't set the last piece to DND unless
1239       every file using that piece is DND */
1240    lastPieceDND = dnd;
1241    for( i=fileIndex+1; lastPieceDND && i<tor->info.fileCount; ++i ) {
1242        if( tor->info.files[i].firstPiece != lastPiece )
1243            break;
1244        lastPieceDND = tor->info.files[i].dnd;
1245    }
1246
1247    if( firstPiece == lastPiece )
1248    {
1249        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
1250    }
1251    else
1252    {
1253        tor->info.pieces[firstPiece].dnd = firstPieceDND;
1254        tor->info.pieces[lastPiece].dnd = lastPieceDND;
1255        for( i=firstPiece+1; i<lastPiece; ++i )
1256            tor->info.pieces[i].dnd = dnd;
1257    }
1258
1259    tr_cpInvalidateDND ( tor->completion );
1260
1261    saveFastResumeSoon( tor );
1262
1263    tr_torrentUnlock( tor );
1264}
1265
1266void
1267tr_torrentSetFileDLs ( tr_torrent  * tor,
1268                       int         * files,
1269                       int           fileCount,
1270                       int           doDownload )
1271{
1272    int i;
1273    for( i=0; i<fileCount; ++i )
1274        tr_torrentSetFileDL( tor, files[i], doDownload );
1275}
1276
1277/***
1278****
1279***/
1280
1281int _tr_block( const tr_torrent * tor, int index, int begin )
1282{
1283    const tr_info * inf = &tor->info;
1284    return index * ( inf->pieceSize / tor->blockSize ) +
1285        begin / tor->blockSize;
1286}
Note: See TracBrowser for help on using the repository browser.