source: trunk/libtransmission/torrent.c @ 3201

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

round 2 of minor revisions to tr_stat

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