source: trunk/libtransmission/torrent.c @ 3197

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

some experimental code. (1) try to improve throughput to peers. (2) add first draft of new tr_stat fields requested by BentMyWookie? (3) raise the per-torrent peer limit to 100 to match LibTorrent?'s defaults

  • Property svn:keywords set to Date Rev Author Id
File size: 33.6 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 3197 2007-09-27 03:03:38Z 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->nonDndSize = 0;
818        s->nonDndAvailable = 0;
819
820        for( i=0; i<tor->info.pieceCount; ++i ) {
821            if( !tor->info.pieces[i].dnd ) {
822                s->nonDndSize += tor->info.pieceSize;
823                if( tr_bitfieldHas( available, i ) )
824                    s->nonDndAvailable += 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.