source: trunk/libtransmission/torrent.c @ 3155

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