source: trunk/libtransmission/torrent.c @ 3178

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

modify the tr_stat struct as hashed out by charles_ and BentMyWookie?. sync gtk, ipc, and wx clients.

  • Property svn:keywords set to Date Rev Author Id
File size: 33.0 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 3178 2007-09-26 01:55:04Z 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    s->ratio = s->downloadedEver ? s->uploadedEver / (float)s->downloadedEver
813                                 : TR_RATIO_NA;
814   
815    tr_torrentUnlock( tor );
816
817    return s;
818}
819
820/***
821****
822***/
823
824static uint64_t
825fileBytesCompleted ( const tr_torrent * tor, int fileIndex )
826{
827    const tr_file * file     =  &tor->info.files[fileIndex];
828    const uint64_t firstBlock       =  file->offset / tor->blockSize;
829    const uint64_t firstBlockOffset =  file->offset % tor->blockSize;
830    const uint64_t lastOffset       =  file->length ? (file->length-1) : 0;
831    const uint64_t lastBlock        = (file->offset + lastOffset) / tor->blockSize;
832    const uint64_t lastBlockOffset  = (file->offset + lastOffset) % tor->blockSize;
833    uint64_t haveBytes = 0;
834
835    assert( tor != NULL );
836    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
837    assert( file->offset + file->length <= tor->info.totalSize );
838    assert( ( (int)firstBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
839    assert( ( (int)lastBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
840    assert( firstBlock <= lastBlock );
841    assert( (int)tr_torBlockPiece( tor, firstBlock ) == file->firstPiece );
842    assert( (int)tr_torBlockPiece( tor, lastBlock ) == file->lastPiece );
843
844    if( firstBlock == lastBlock )
845    {
846        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
847            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
848    }
849    else
850    {
851        uint64_t i;
852
853        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
854            haveBytes += tor->blockSize - firstBlockOffset;
855
856        for( i=firstBlock+1; i<lastBlock; ++i )
857            if( tr_cpBlockIsComplete( tor->completion, i ) )
858               haveBytes += tor->blockSize;
859
860        if( tr_cpBlockIsComplete( tor->completion, lastBlock ) )
861            haveBytes += lastBlockOffset + 1;
862    }
863
864    return haveBytes;
865}
866
867tr_file_stat *
868tr_torrentFiles( const tr_torrent * tor, int * fileCount )
869{
870    int i;
871    const int n = tor->info.fileCount;
872    tr_file_stat * files = tr_new0( tr_file_stat, n );
873    tr_file_stat * walk = files;
874
875    for( i=0; i<n; ++i, ++walk )
876    {
877        const tr_file * file = tor->info.files + i;
878        cp_status_t cp;
879
880        walk->bytesCompleted = fileBytesCompleted( tor, i );
881
882        walk->progress = file->length
883            ? walk->bytesCompleted / (float)file->length
884            : 1.0;
885
886        if( walk->bytesCompleted >= file->length )
887            cp = TR_CP_COMPLETE;
888        else if( tor->info.files[i].dnd )
889            cp = TR_CP_DONE;
890        else
891            cp = TR_CP_INCOMPLETE;
892
893        walk->completionStatus = cp;
894    }
895
896    *fileCount = n;
897
898    return files;
899}
900
901void
902tr_torrentFilesFree( tr_file_stat * files, int fileCount UNUSED )
903{
904    tr_free( files );
905}
906
907/***
908****
909***/
910
911tr_peer_stat *
912tr_torrentPeers( const tr_torrent * tor, int * peerCount )
913{
914    return tr_peerMgrPeerStats( tor->handle->peerMgr,
915                                tor->info.hash, peerCount );
916}
917
918void tr_torrentPeersFree( tr_peer_stat * peers, int peerCount UNUSED )
919{
920    tr_free( peers );
921}
922
923void tr_torrentAvailability( const tr_torrent * tor, int8_t * tab, int size )
924{
925    return tr_peerMgrTorrentAvailability( tor->handle->peerMgr,
926                                          tor->info.hash,
927                                          tab, size );
928}
929
930void tr_torrentAmountFinished( const tr_torrent * tor, float * tab, int size )
931{
932    int i;
933    float interval;
934    tr_torrentLock( tor );
935
936    interval = (float)tor->info.pieceCount / (float)size;
937    for( i = 0; i < size; i++ )
938    {
939        int piece = i * interval;
940        tab[i] = tr_cpPercentBlocksInPiece( tor->completion, piece );
941    }
942
943    tr_torrentUnlock( tor );
944}
945
946void
947tr_torrentResetTransferStats( tr_torrent * tor )
948{
949    tr_torrentLock( tor );
950
951    tor->downloadedPrev += tor->downloadedCur;
952    tor->downloadedCur   = 0;
953    tor->uploadedPrev   += tor->uploadedCur;
954    tor->uploadedCur     = 0;
955    tor->corruptPrev    += tor->corruptCur;
956    tor->corruptCur      = 0;
957
958    tr_torrentUnlock( tor );
959}
960
961
962void
963tr_torrentSetHasPiece( tr_torrent * tor, int pieceIndex, int has )
964{
965    tr_torrentLock( tor );
966
967    if( has )
968        tr_cpPieceAdd( tor->completion, pieceIndex );
969    else
970        tr_cpPieceRem( tor->completion, pieceIndex );
971
972    tr_torrentUnlock( tor );
973}
974
975void tr_torrentRemoveSaved( tr_torrent * tor )
976{
977    tr_metainfoRemoveSaved( tor->info.hashString, tor->handle->tag );
978}
979
980void tr_torrentRecheck( tr_torrent * tor )
981{
982    if( !tor->uncheckedPieces )
983        tor->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
984    tr_bitfieldAddRange( tor->uncheckedPieces, 0, tor->info.pieceCount );
985
986    tr_ioRecheckAdd( tor, recheckDoneCB, tor->runStatus );
987}
988
989/***
990****
991***/
992
993void
994tr_torrentStart( tr_torrent * tor )
995{
996    tr_ioRecheckAdd( tor, recheckDoneCB, TR_RUN_RUNNING );
997}
998
999static void
1000tr_torrentFree( tr_torrent * tor )
1001{
1002    tr_torrent * t;
1003    tr_handle * h = tor->handle;
1004    tr_info * inf = &tor->info;
1005
1006    assert( tor != NULL );
1007    assert( tor->runStatus == TR_RUN_STOPPED );
1008
1009    tr_sharedLock( h->shared );
1010
1011    tr_peerMgrRemoveTorrent( h->peerMgr, tor->info.hash );
1012
1013    tr_lockFree( tor->lock );
1014    tr_cpClose( tor->completion );
1015
1016    tr_rcClose( tor->upload );
1017    tr_rcClose( tor->download );
1018    tr_rcClose( tor->swarmspeed );
1019
1020    tr_trackerUnsubscribe( tor->tracker, tor->trackerSubscription );
1021    tr_trackerFree( tor->tracker );
1022    tor->tracker = NULL;
1023
1024    tr_free( tor->destination );
1025
1026    if( tor == h->torrentList )
1027        h->torrentList = tor->next;
1028    else for( t=h->torrentList; t!=NULL; t=t->next ) {
1029        if( t->next == tor ) {
1030            t->next = tor->next;
1031            break;
1032        }
1033    }
1034
1035    assert( h->torrentCount >= 1 );
1036    h->torrentCount--;
1037
1038    tr_inf( "closing torrent %s... %d torrents left",
1039            tor->info.name, h->torrentCount );
1040
1041    tr_metainfoFree( inf );
1042    tr_free( tor );
1043
1044    tr_sharedUnlock( h->shared );
1045}
1046
1047static int
1048freeWhenStopped( void * vtor )
1049{
1050    tr_torrent * tor = vtor;
1051
1052    if( tor->runStatus != TR_RUN_STOPPED ) /* keep waiting */
1053        return TRUE;
1054
1055    tr_torrentFree( tor );
1056    return FALSE;
1057}
1058
1059static void
1060tr_torrentStopImpl( void * vtor )
1061{
1062    tr_torrent * tor = vtor;
1063
1064    switch( tor->runStatus )
1065    {
1066        case TR_RUN_CHECKING_WAIT:
1067        case TR_RUN_CHECKING:
1068            tor->doStopAfterHashCheck = 1;
1069            tr_ioRecheckRemove( tor );
1070            break;
1071
1072        case TR_RUN_RUNNING:
1073            saveFastResumeNow( tor );
1074            tr_peerMgrStopTorrent( tor->handle->peerMgr, tor->info.hash );
1075            tor->runStatus = TR_RUN_STOPPING;
1076            tr_trackerStop( tor->tracker );
1077            tr_ioClose( tor );
1078            break;
1079
1080        case TR_RUN_STOPPING:
1081        case TR_RUN_STOPPED:
1082            break;
1083    }
1084}
1085
1086void
1087tr_torrentStop( tr_torrent * tor )
1088{
1089    tr_runInEventThread( tor->handle, tr_torrentStopImpl, tor );
1090}
1091
1092void
1093tr_torrentClose( tr_torrent * tor )
1094{
1095    tor->runStatusToSave = tor->runStatus;
1096    tor->runStatusToSaveIsSet = TRUE;
1097    tr_torrentStop( tor );
1098    tr_timerNew( tor->handle, freeWhenStopped, tor, 250 );
1099}
1100
1101void
1102tr_torrentRecheckCompleteness( tr_torrent * tor )
1103{
1104    cp_status_t cpStatus;
1105
1106    tr_torrentLock( tor );
1107
1108    cpStatus = tr_cpGetStatus( tor->completion );
1109    if( cpStatus != tor->cpStatus ) {
1110        tor->cpStatus = cpStatus;
1111        tor->hasChangedState = tor->cpStatus;  /* tell the client... */
1112        if( (cpStatus == TR_CP_COMPLETE) /* ...and if we're complete */
1113            && tor->downloadedCur ) {        /* and it just happened */
1114            tr_trackerCompleted( tor->tracker ); /* tell the tracker */
1115        }
1116        tr_ioClose( tor );
1117        saveFastResumeSoon( tor );
1118    }
1119    tr_torrentUnlock( tor );
1120}
1121
1122/**
1123***  File priorities
1124**/
1125
1126void
1127tr_torrentSetFilePriority( tr_torrent   * tor,
1128                           int            fileIndex,
1129                           tr_priority_t  priority )
1130{
1131    int i;
1132    tr_file * file;
1133
1134    tr_torrentLock( tor );
1135
1136    assert( tor != NULL );
1137    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
1138    assert( priority==TR_PRI_LOW || priority==TR_PRI_NORMAL || priority==TR_PRI_HIGH );
1139
1140    file = &tor->info.files[fileIndex];
1141    file->priority = priority;
1142    for( i=file->firstPiece; i<=file->lastPiece; ++i )
1143      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
1144
1145    tr_dbg ( "Setting file #%d (pieces %d-%d) priority to %d (%s)",
1146             fileIndex, file->firstPiece, file->lastPiece,
1147             priority, tor->info.files[fileIndex].name );
1148
1149    saveFastResumeSoon( tor );
1150
1151    tr_torrentUnlock( tor );
1152}
1153
1154void
1155tr_torrentSetFilePriorities( tr_torrent     * tor,
1156                             int            * files,
1157                             int              fileCount,
1158                             tr_priority_t    priority )
1159{
1160    int i;
1161    for( i=0; i<fileCount; ++i )
1162        tr_torrentSetFilePriority( tor, files[i], priority );
1163}
1164
1165tr_priority_t
1166tr_torrentGetFilePriority( const tr_torrent *  tor, int file )
1167{
1168    tr_priority_t ret;
1169
1170    tr_torrentLock( tor );
1171    assert( tor != NULL );
1172    assert( 0<=file && file<tor->info.fileCount );
1173    ret = tor->info.files[file].priority;
1174    tr_torrentUnlock( tor );
1175
1176    return ret;
1177}
1178
1179
1180tr_priority_t*
1181tr_torrentGetFilePriorities( const tr_torrent * tor )
1182{
1183    int i;
1184    tr_priority_t * p;
1185
1186    tr_torrentLock( tor );
1187    p = tr_new0( tr_priority_t, tor->info.fileCount );
1188    for( i=0; i<tor->info.fileCount; ++i )
1189        p[i] = tor->info.files[i].priority;
1190    tr_torrentUnlock( tor );
1191
1192    return p;
1193}
1194
1195/**
1196***  File DND
1197**/
1198
1199int
1200tr_torrentGetFileDL( const tr_torrent * tor,
1201                     int                file )
1202{
1203    int doDownload;
1204    tr_torrentLock( tor );
1205
1206    assert( 0<=file && file<tor->info.fileCount );
1207    doDownload = !tor->info.files[file].dnd;
1208
1209    tr_torrentUnlock( tor );
1210    return doDownload != 0;
1211}
1212
1213void
1214tr_torrentSetFileDL( tr_torrent  * tor,
1215                     int           fileIndex,
1216                     int           doDownload )
1217{
1218    tr_file * file;
1219    const int dnd = !doDownload;
1220    int firstPiece, firstPieceDND;
1221    int lastPiece, lastPieceDND;
1222    int i;
1223
1224    tr_torrentLock( tor );
1225
1226    file = &tor->info.files[fileIndex];
1227    file->dnd = dnd;
1228    firstPiece = file->firstPiece;
1229    lastPiece = file->lastPiece;
1230
1231    /* can't set the first piece to DND unless
1232       every file using that piece is DND */
1233    firstPieceDND = dnd;
1234    for( i=fileIndex-1; firstPieceDND && i>=0; --i ) {
1235        if( tor->info.files[i].lastPiece != firstPiece )
1236            break;
1237        firstPieceDND = tor->info.files[i].dnd;
1238    }
1239
1240    /* can't set the last piece to DND unless
1241       every file using that piece is DND */
1242    lastPieceDND = dnd;
1243    for( i=fileIndex+1; lastPieceDND && i<tor->info.fileCount; ++i ) {
1244        if( tor->info.files[i].firstPiece != lastPiece )
1245            break;
1246        lastPieceDND = tor->info.files[i].dnd;
1247    }
1248
1249    if( firstPiece == lastPiece )
1250    {
1251        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
1252    }
1253    else
1254    {
1255        tor->info.pieces[firstPiece].dnd = firstPieceDND;
1256        tor->info.pieces[lastPiece].dnd = lastPieceDND;
1257        for( i=firstPiece+1; i<lastPiece; ++i )
1258            tor->info.pieces[i].dnd = dnd;
1259    }
1260
1261    tr_cpInvalidateDND ( tor->completion );
1262
1263    saveFastResumeSoon( tor );
1264
1265    tr_torrentUnlock( tor );
1266}
1267
1268void
1269tr_torrentSetFileDLs ( tr_torrent  * tor,
1270                       int         * files,
1271                       int           fileCount,
1272                       int           doDownload )
1273{
1274    int i;
1275    for( i=0; i<fileCount; ++i )
1276        tr_torrentSetFileDL( tor, files[i], doDownload );
1277}
1278
1279/***
1280****
1281***/
1282
1283int _tr_block( const tr_torrent * tor, int index, int begin )
1284{
1285    const tr_info * inf = &tor->info;
1286    return index * ( inf->pieceSize / tor->blockSize ) +
1287        begin / tor->blockSize;
1288}
1289
1290uint64_t
1291tr_pieceOffset( const tr_torrent * tor, int index, int begin, int length )
1292{
1293    uint64_t ret;
1294    ret = tor->info.pieceSize;
1295    ret *= index;
1296    ret += begin;
1297    ret += length;
1298    return ret;
1299}
1300
Note: See TracBrowser for help on using the repository browser.