source: trunk/libtransmission/torrent.c @ 3111

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

remove the backwards compatability typedefs at BentMyWookie?'s suggestion. update libT, gtk, daemon, and cli accordingly...

  • Property svn:keywords set to Date Rev Author Id
File size: 32.9 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 3111 2007-09-20 20:14:13Z charles $
3 *
4 * Copyright (c) 2005-2007 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <assert.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29
30#include <sys/types.h>
31
32#include "transmission.h"
33#include "completion.h"
34#include "crypto.h" /* for tr_sha1 */
35#include "fastresume.h"
36#include "handshake.h"
37#include "inout.h"
38#include "metainfo.h"
39#include "net.h" /* tr_netNtop */
40#include "peer-mgr.h"
41#include "platform.h"
42#include "ratecontrol.h"
43#include "shared.h"
44#include "tracker.h"
45#include "trcompat.h" /* for strlcpy */
46#include "trevent.h"
47#include "utils.h"
48
49/***
50****
51***/
52
53int
54tr_torrentExists( tr_handle       * handle,
55                  const uint8_t   * torrentHash )
56{
57    return tr_torrentFindFromHash( handle, torrentHash ) != NULL;
58}
59
60tr_torrent*
61tr_torrentFindFromHash( tr_handle      * handle,
62                        const uint8_t  * torrentHash )
63{
64    tr_torrent * tor;
65
66    for( tor = handle->torrentList; tor; tor = tor->next )
67        if( !memcmp( tor->info.hash, torrentHash, SHA_DIGEST_LENGTH ) )
68            return tor;
69
70    return NULL;
71}
72
73tr_torrent*
74tr_torrentFindFromObfuscatedHash( tr_handle      * handle,
75                                  const uint8_t  * obfuscatedTorrentHash )
76{
77    tr_torrent * tor;
78
79    for( tor = handle->torrentList; tor; tor = tor->next )
80        if( !memcmp( tor->obfuscatedHash, obfuscatedTorrentHash, SHA_DIGEST_LENGTH ) )
81            return tor;
82
83    return NULL;
84}
85
86/***
87****  LOCKS
88***/
89
90void
91tr_torrentLock( const tr_torrent * tor )
92{
93    tr_lockLock ( (tr_lock*)tor->lock );
94}
95
96void
97tr_torrentUnlock( const tr_torrent * tor )
98{
99    tr_lockUnlock ( (tr_lock*)tor->lock );
100}
101
102/***
103****  PER-TORRENT UL / DL SPEEDS
104***/
105
106void
107tr_torrentSetSpeedMode( tr_torrent   * tor,
108                        int            up_or_down,
109                        tr_speedlimit  mode )
110{
111    tr_speedlimit * limit = up_or_down==TR_UP
112        ? &tor->uploadLimitMode
113        : &tor->downloadLimitMode;
114    *limit = mode;
115}
116
117tr_speedlimit
118tr_torrentGetSpeedMode( const tr_torrent * tor,
119                        int                up_or_down)
120{
121    return up_or_down==TR_UP ? tor->uploadLimitMode
122                             : tor->downloadLimitMode;
123}
124
125void
126tr_torrentSetSpeedLimit( tr_torrent   * tor,
127                         int            up_or_down,
128                         int            single_KiB_sec )
129{
130    tr_ratecontrol * rc = up_or_down==TR_UP ? tor->upload : tor->download;
131    tr_rcSetLimit( rc, single_KiB_sec );
132}
133
134int
135tr_torrentGetSpeedLimit( const tr_torrent  * tor,
136                         int                 up_or_down )
137{
138    tr_ratecontrol * rc = up_or_down==TR_UP ? tor->upload : tor->download;
139    return tr_rcGetLimit( rc );
140}
141
142/***
143****
144***/
145
146static void
147onTrackerResponse( void * tracker UNUSED, void * vevent, void * user_data )
148{
149    tr_torrent * tor = (tr_torrent *) user_data;
150    tr_tracker_event_t * event = (tr_tracker_event_t *) vevent;
151
152    switch( event->messageType )
153    {
154        case TR_TRACKER_PEERS:
155            tr_peerMgrAddPeers( tor->handle->peerMgr,
156                                tor->info.hash,
157                                TR_PEER_FROM_TRACKER,
158                                event->peerCompact,
159                                event->peerCount );
160            break;
161
162        case TR_TRACKER_WARNING:
163            tr_err( "Tracker: Warning - %s", event->text );
164            tor->error = TR_ERROR_TC_WARNING;
165            strlcpy( tor->errorString, event->text, sizeof(tor->errorString) );
166            break;
167
168        case TR_TRACKER_ERROR:
169            tr_err( "Tracker: Error - %s", event->text );
170            tor->error = TR_ERROR_TC_ERROR;
171            strlcpy( tor->errorString, event->text, sizeof(tor->errorString) );
172            break;
173
174        case TR_TRACKER_STOPPED:
175            //assert( tor->runStatus == TR_RUN_STOPPING );
176            tor->runStatus = TR_RUN_STOPPED;
177            break;
178    }
179}
180
181/***
182****
183****  TORRENT INSTANTIATION
184****
185***/
186
187static int
188getBytePiece( const tr_info * info, uint64_t byteOffset )
189{
190    assert( info != NULL );
191    assert( info->pieceSize != 0 );
192
193    return byteOffset / info->pieceSize;
194}
195
196static void
197initFilePieces ( tr_info * info, int fileIndex )
198{
199    tr_file * file = &info->files[fileIndex];
200    uint64_t firstByte, lastByte;
201
202    assert( info != NULL );
203    assert( 0<=fileIndex && fileIndex<info->fileCount );
204
205    file = &info->files[fileIndex];
206    firstByte = file->offset;
207    lastByte = firstByte + (file->length ? file->length-1 : 0);
208    file->firstPiece = getBytePiece( info, firstByte );
209    file->lastPiece = getBytePiece( info, lastByte );
210}
211
212static tr_priority_t
213calculatePiecePriority ( const tr_torrent * tor,
214                         int                piece )
215{
216    int i;
217    tr_priority_t priority = TR_PRI_NORMAL;
218
219    for( i=0; i<tor->info.fileCount; ++i )
220    {
221        const tr_file * file = &tor->info.files[i];
222        if ( file->firstPiece <= piece
223          && file->lastPiece  >= piece
224          && file->priority   >  priority)
225              priority = file->priority;
226
227        /* when dealing with multimedia files, getting the first and
228           last pieces can sometimes allow you to preview it a bit
229           before it's fully downloaded... */
230        if ( file->priority >= TR_PRI_NORMAL )
231            if ( file->firstPiece == piece || file->lastPiece == piece )
232                priority = TR_PRI_HIGH;
233    }
234
235    return priority;
236}
237
238static void
239tr_torrentInitFilePieces( tr_torrent * tor )
240{
241    int i;
242    uint64_t offset = 0;
243
244    assert( tor != NULL );
245
246    for( i=0; i<tor->info.fileCount; ++i ) {
247      tor->info.files[i].offset = offset;
248      offset += tor->info.files[i].length;
249      initFilePieces( &tor->info, i );
250    }
251
252    for( i=0; i<tor->info.pieceCount; ++i )
253        tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
254}
255
256static void recheckCpState( tr_torrent * );
257
258static void
259tr_torrentStartImpl( tr_torrent * tor )
260{
261    assert( tor != NULL );
262    assert( tor->runStatus == TR_RUN_RUNNING );
263
264    *tor->errorString = '\0';
265    tr_torrentResetTransferStats( tor );
266    recheckCpState( tor );
267    tor->startDate = tr_date();
268    tr_trackerStart( tor->tracker );
269    tr_peerMgrStartTorrent( tor->handle->peerMgr, tor->info.hash );
270}
271
272static void
273recheckDoneCB( tr_torrent * tor )
274{
275    recheckCpState( tor );
276
277    if( tor->runStatus == TR_RUN_RUNNING )
278        tr_torrentStartImpl( tor );
279}
280
281static void
282torrentRealInit( tr_handle  * h,
283                 tr_torrent * tor,
284                 const char * destination,
285                 int          flags )
286{
287    uint64_t loaded;
288    uint64_t t;
289    tr_bitfield * uncheckedPieces;
290    tr_info * info = &tor->info;
291   
292    tor->info.flags |= flags;
293
294    tr_sharedLock( h->shared );
295
296    tor->destination = tr_strdup( destination );
297
298    tor->handle   = h;
299    tor->azId     = h->azId;
300    tor->hasChangedState = -1;
301    tor->pexDisabled = 0;
302
303    tor->runStatusToSaveIsSet = FALSE;
304
305    /**
306     * Decide on a block size.  constraints:
307     * (1) most clients decline requests over 16 KiB
308     * (2) pieceSize must be a multiple of block size
309     */
310    tor->blockSize = info->pieceSize;
311    while( tor->blockSize > (1024*16) )
312        tor->blockSize /= 2;
313
314    tor->lastPieceSize = info->totalSize % info->pieceSize;
315
316    if( !tor->lastPieceSize )
317         tor->lastPieceSize = info->pieceSize;
318
319    tor->lastBlockSize = info->totalSize % tor->blockSize;
320
321    if( !tor->lastBlockSize )
322         tor->lastBlockSize = tor->blockSize;
323
324    tor->blockCount =
325        ( info->totalSize + tor->blockSize - 1 ) / tor->blockSize;
326
327    tor->blockCountInPiece =
328        info->pieceSize / tor->blockSize;
329
330    tor->blockCountInLastPiece =
331        ( tor->lastPieceSize + tor->blockSize - 1 ) / tor->blockSize;
332
333    /* check our work */
334    assert( ( info->pieceSize % tor->blockSize ) == 0 );
335    t = info->pieceCount - 1;
336    t *= info->pieceSize;
337    t += tor->lastPieceSize;
338    assert( t == info->totalSize );
339    t = tor->blockCount - 1;
340    t *= tor->blockSize;
341    t += tor->lastBlockSize;
342    assert( t == info->totalSize );
343    t = info->pieceCount - 1;
344    t *= tor->blockCountInPiece;
345    t += tor->blockCountInLastPiece;
346    assert( t == (uint64_t)tor->blockCount );
347
348    tor->completion = tr_cpInit( tor );
349
350    tr_torrentInitFilePieces( tor );
351
352    tor->lock = tr_lockNew( );
353
354    tor->upload         = tr_rcInit();
355    tor->download       = tr_rcInit();
356    tor->swarmspeed     = tr_rcInit();
357
358    tr_sha1( tor->obfuscatedHash, "req2", 4,
359                                  info->hash, SHA_DIGEST_LENGTH,
360                                  NULL );
361 
362    /* We have a new torrent */
363    tor->publicPort = tr_sharedGetPublicPort( h->shared );
364
365    tr_sharedUnlock( h->shared );
366
367    tr_peerMgrAddTorrent( h->peerMgr, tor );
368
369    if( !h->isPortSet )
370        tr_setBindPort( h, TR_DEFAULT_PORT );
371
372    assert( !tor->downloadedCur );
373    assert( !tor->uploadedCur );
374
375    tor->error   = TR_OK;
376
377    uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
378    loaded = tr_fastResumeLoad( tor, uncheckedPieces );
379
380    /* the `paused' flag has highest precedence...
381       after that, the fastresume setting is used...
382       if that's not found, default to RUNNING */
383    if( flags & TR_FLAG_PAUSED )
384        tor->runStatus = TR_RUN_STOPPED;
385    else if( !(loaded & TR_FR_RUN ) )
386        tor->runStatus = TR_RUN_RUNNING;
387
388    if( tr_bitfieldIsEmpty( uncheckedPieces ) )
389        tr_bitfieldFree( uncheckedPieces );
390    else
391        tor->uncheckedPieces = uncheckedPieces;
392
393
394    if( !(loaded & TR_FR_SPEEDLIMIT ) ) {
395        int limit, enabled;
396        tr_getGlobalSpeedLimit( tor->handle, TR_UP, &enabled, &limit );
397        tr_torrentSetSpeedLimit( tor, TR_UP, limit );
398        tr_getGlobalSpeedLimit( tor->handle, TR_DOWN, &enabled, &limit );
399        tr_torrentSetSpeedLimit( tor, TR_DOWN, limit );
400    }
401
402    tor->cpStatus = tr_cpGetStatus( tor->completion );
403
404    tor->tracker = tr_trackerNew( tor );
405    tor->trackerSubscription = tr_trackerSubscribe( tor->tracker, onTrackerResponse, tor );
406
407    tr_sharedLock( h->shared );
408    tor->next = h->torrentList;
409    h->torrentList = tor;
410    h->torrentCount++;
411    tr_sharedUnlock( h->shared );
412
413    tr_ioRecheckAdd( tor, recheckDoneCB, tor->runStatus );
414}
415
416static int
417pathIsInUse ( const tr_handle   * h,
418              const char        * destination,
419              const char        * name )
420{
421    const tr_torrent * tor;
422   
423    for( tor=h->torrentList; tor; tor=tor->next )
424        if( !strcmp( destination, tor->destination )
425         && !strcmp( name, tor->info.name ) )
426            return TRUE;
427
428    return FALSE;
429}
430
431static int
432hashExists( const tr_handle   * h,
433            const uint8_t     * hash )
434{
435    const tr_torrent * tor;
436
437    for( tor=h->torrentList; tor; tor=tor->next )
438        if( !memcmp( hash, tor->info.hash, SHA_DIGEST_LENGTH ) )
439            return TRUE;
440
441    return FALSE;
442}
443
444static int
445infoCanAdd( const tr_handle   * h,
446            const char        * destination,
447            const tr_info     * info )
448{
449    if( hashExists( h, info->hash ) )
450        return TR_EDUPLICATE;
451
452    if( destination && pathIsInUse( h, destination, info->name ) )
453        return TR_EDUPLICATE;
454
455    return TR_OK;
456}
457
458int
459tr_torrentParse( const tr_handle  * h,
460                 const char       * path,
461                 const char       * destination,
462                 tr_info          * setme_info )
463{
464    int ret, doFree;
465    tr_info tmp;
466
467    if( setme_info == NULL )
468        setme_info = &tmp;
469
470    memset( setme_info, 0, sizeof( tr_info ) );
471    ret = tr_metainfoParseFile( setme_info, h->tag, path, FALSE );
472    doFree = !ret && (setme_info == &tmp);
473
474    if( ret == TR_OK )
475        ret = infoCanAdd( h, destination, setme_info );
476
477    if( doFree )
478        tr_metainfoFree( &tmp );
479
480    return ret;
481}
482 
483tr_torrent *
484tr_torrentInit( tr_handle   * h,
485                const char  * path,
486                const char  * destination,
487                int           flags,
488                int         * error )
489{
490    int val;
491    int tmpError;
492    tr_torrent * tor = NULL;
493
494    if( !error )
495         error = &tmpError;
496
497    if(( val = tr_torrentParse( h, path, destination, NULL )))
498        *error = val;
499    else if(!(( tor = tr_new0( tr_torrent, 1 ))))
500        *error = TR_EOTHER;
501    else {
502        tr_metainfoParseFile( &tor->info, h->tag, path, TR_FLAG_SAVE & flags );
503        torrentRealInit( h, tor, destination, flags );
504    }
505
506    return tor;
507}
508
509int
510tr_torrentParseHash( const tr_handle  * h,
511                     const char       * hashStr,
512                     const char       * destination,
513                     tr_info          * setme_info )
514{
515    int ret, doFree;
516    tr_info tmp;
517
518    if( setme_info == NULL )
519        setme_info = &tmp;
520
521    memset( setme_info, 0, sizeof( tr_info ) );
522    ret = tr_metainfoParseHash( setme_info, h->tag, hashStr );
523    doFree = !ret && (setme_info == &tmp);
524
525    if( ret == TR_OK )
526        ret = infoCanAdd( h, destination, setme_info );
527
528    if( doFree )
529        tr_metainfoFree( &tmp );
530
531    return ret;
532}
533
534tr_torrent *
535tr_torrentInitSaved( tr_handle    * h,
536                     const char   * hashStr,
537                     const char   * destination,
538                     int            flags,
539                     int          * error )
540{
541    int val;
542    int tmpError;
543    tr_torrent * tor = NULL;
544
545    if( !error )
546         error = &tmpError;
547
548    if(( val = tr_torrentParseHash( h, hashStr, destination, NULL )))
549        *error = val;
550    else if(!(( tor = tr_new0( tr_torrent, 1 ))))
551        *error = TR_EOTHER;
552    else {
553        tr_metainfoParseHash( &tor->info, h->tag, hashStr );
554        torrentRealInit( h, tor, destination, (TR_FLAG_SAVE|flags) );
555    }
556
557    return tor;
558}
559
560static int
561tr_torrentParseData( const tr_handle  * h,
562                     const uint8_t    * data,
563                     size_t             size,
564                     const char       * destination,
565                     tr_info          * setme_info )
566{
567    int ret, doFree;
568    tr_info tmp;
569
570    if( setme_info == NULL )
571        setme_info = &tmp;
572
573    memset( setme_info, 0, sizeof( tr_info ) );
574    ret = tr_metainfoParseData( setme_info, h->tag, data, size, FALSE );
575    doFree = !ret && (setme_info == &tmp);
576
577    if( ret == TR_OK )
578        ret = infoCanAdd( h, destination, setme_info );
579
580    if( doFree )
581        tr_metainfoFree( &tmp );
582
583    return ret;
584}
585
586tr_torrent *
587tr_torrentInitData( tr_handle      * h,
588                    const uint8_t  * data,
589                    size_t           size,
590                    const char     * destination,
591                    int              flags,
592                    int            * error )
593{
594    int val;
595    int tmpError;
596    tr_torrent * tor = NULL;
597
598    if( !error )
599         error = &tmpError;
600
601    if(( val = tr_torrentParseData( h, data, size, destination, NULL )))
602        *error = val;
603    else if(!(( tor = tr_new0( tr_torrent, 1 ))))
604        *error = TR_EOTHER;
605    else {
606        tr_metainfoParseData( &tor->info, h->tag, data, size, TR_FLAG_SAVE & flags );
607        torrentRealInit( h, tor, destination, flags );
608    }
609
610    return tor;
611}
612
613const tr_info *
614tr_torrentInfo( const tr_torrent * tor )
615{
616    return &tor->info;
617}
618
619/***
620****
621***/
622
623#if 0
624int tr_torrentScrape( tr_torrent * tor, int * s, int * l, int * d )
625{
626    return tr_trackerScrape( tor, s, l, d );
627}
628#endif
629
630static int
631saveFastResumeNow( void * vtor )
632{
633    tr_torrent * tor = (tr_torrent *) vtor;
634
635    tr_fastResumeSave( tor );
636    recheckCpState( tor );
637
638    tr_timerFree( &tor->saveTimer );
639    return FALSE;
640}
641
642static void
643saveFastResumeSoon( void * vtor )
644{
645    tr_torrent * tor = (tr_torrent *) vtor;
646
647    if( tor->saveTimer == NULL )
648        tor->saveTimer = tr_timerNew( tor->handle, saveFastResumeNow, tor, 1000 );
649}
650
651/**
652***
653**/
654
655void
656tr_torrentSetFolder( tr_torrent * tor, const char * path )
657{
658    tr_free( tor->destination );
659    tor->destination = tr_strdup( path );
660    saveFastResumeSoon( tor );
661}
662
663const char*
664tr_torrentGetFolder( const tr_torrent * tor )
665{
666    return tor->destination;
667}
668
669void
670tr_torrentChangeMyPort( tr_torrent * tor, int port )
671{
672    tr_torrentLock( tor );
673
674    tor->publicPort = port;
675
676    if( tor->tracker )
677        tr_trackerChangeMyPort( tor->tracker );
678
679    tr_torrentUnlock( tor );
680}
681
682
683void
684tr_torrentDisablePex( tr_torrent * tor, int disable )
685{
686    assert( tor != NULL );
687    assert( disable==0 || disable==1 );
688    //assert( tor->runStatus != TR_RUN_RUNNING );
689
690    /* pex is ALWAYS disabled for private torrents */
691    if( tor->info.flags & TR_FLAG_PRIVATE )
692        disable = TRUE;
693
694    tor->pexDisabled = disable;
695}
696
697static int tr_didStateChangeTo ( tr_torrent * tor, int status )
698{
699    int ret;
700
701    tr_torrentLock( tor );
702    if (( ret = tor->hasChangedState == status ))
703        tor->hasChangedState = -1;
704    tr_torrentUnlock( tor );
705
706    return ret;
707}
708
709int tr_getIncomplete( tr_torrent * tor )
710{
711    return tr_didStateChangeTo( tor, TR_CP_INCOMPLETE );
712}
713int tr_getDone( tr_torrent * tor )
714{
715    return tr_didStateChangeTo( tor, TR_CP_DONE );
716}
717int tr_getComplete( tr_torrent * tor )
718{
719    return tr_didStateChangeTo( tor, TR_CP_COMPLETE );
720}
721
722void
723tr_manualUpdate( tr_torrent * tor )
724{
725    if( tor->runStatus == TR_RUN_RUNNING )
726    tr_trackerReannounce( tor->tracker );
727}
728int
729tr_torrentCanManualUpdate( const tr_torrent * tor )
730{
731    return ( tor != NULL )
732        && ( tor->runStatus == TR_RUN_RUNNING )
733        && ( tr_trackerCanManualAnnounce( tor->tracker ) );
734}
735
736const tr_stat *
737tr_torrentStat( tr_torrent * tor )
738{
739    tr_stat * s;
740    struct tr_tracker * tc;
741
742    tr_torrentLock( tor );
743
744    tor->statCur = ( tor->statCur + 1 ) % 2;
745    s = &tor->stats[tor->statCur];
746
747    s->error  = tor->error;
748    memcpy( s->errorString, tor->errorString,
749            sizeof( s->errorString ) );
750
751    tc = tor->tracker;
752    s->tracker = tr_trackerGetAddress( tor->tracker );
753
754    tr_peerMgrTorrentStats( tor->handle->peerMgr,
755                            tor->info.hash,
756                            &s->peersTotal,
757                            &s->peersConnected,
758                            &s->peersSendingToUs,
759                            &s->peersGettingFromUs,
760                             s->peersFrom );
761
762    s->percentComplete = tr_cpPercentComplete ( tor->completion );
763
764    s->percentDone = tr_cpPercentDone( tor->completion );
765    s->left = tr_cpLeftUntilDone( tor->completion );
766
767    switch( tor->runStatus ) {
768        case TR_RUN_CHECKING_WAIT: s->status = TR_STATUS_CHECK_WAIT; break;
769        case TR_RUN_CHECKING: s->status = TR_STATUS_CHECK; break;
770        case TR_RUN_STOPPING: s->status = TR_STATUS_STOPPING; break;
771        case TR_RUN_STOPPED: s->status = TR_STATUS_STOPPED; break;
772        case TR_RUN_RUNNING: switch( tor->cpStatus ) {
773            case TR_CP_INCOMPLETE: s->status = TR_STATUS_DOWNLOAD; break;
774            case TR_CP_DONE: s->status = TR_STATUS_DONE; break;
775            case TR_CP_COMPLETE: s->status = TR_STATUS_SEED; break;
776        }
777    }
778
779    s->recheckProgress = (tor->uncheckedPieces == NULL)
780        ? 0.0
781        : 1.0 - ((double)tr_bitfieldCountTrueBits(tor->uncheckedPieces) / tor->info.pieceCount);
782
783    s->cpStatus = tor->cpStatus;
784
785    /* tr_rcRate() doesn't make the difference between 'piece'
786       messages and other messages, which causes a non-zero
787       download rate even tough we are not downloading. So we
788       force it to zero not to confuse the user. */
789    s->rateDownload = tor->runStatus==TR_RUN_RUNNING
790        ? tr_rcRate( tor->download )
791        : 0.0;
792    s->rateUpload = tr_rcRate( tor->upload );
793   
794    tr_trackerGetCounts( tc,
795                         &s->completedFromTracker,
796                         &s->leechers, 
797                         &s->seeders );
798
799    s->swarmspeed = tr_rcRate( tor->swarmspeed );
800   
801    s->startDate = tor->startDate;
802    s->activityDate = tor->activityDate;
803
804    s->eta = s->rateDownload < 0.1
805        ? -1.0f
806        : (s->left / s->rateDownload / 1024.0);
807
808    s->corrupt         = tor->corruptCur    + tor->corruptPrev;
809    s->uploaded        = tor->uploadedCur   + tor->uploadedPrev;
810    s->downloaded      = tor->downloadedCur + tor->downloadedPrev;
811    s->downloadedValid = tr_cpDownloadedValid( tor->completion );
812   
813    s->ratio = s->downloaded || s->downloadedValid
814      ? (float)s->uploaded / (float)MAX(s->downloaded, s->downloadedValid)
815      : TR_RATIO_NA; 
816   
817    tr_torrentUnlock( tor );
818
819    return s;
820}
821
822/***
823****
824***/
825
826static uint64_t
827fileBytesCompleted ( const tr_torrent * tor, int fileIndex )
828{
829    const tr_file * file     =  &tor->info.files[fileIndex];
830    const uint64_t firstBlock       =  file->offset / tor->blockSize;
831    const uint64_t firstBlockOffset =  file->offset % tor->blockSize;
832    const uint64_t lastOffset       =  file->length ? (file->length-1) : 0;
833    const uint64_t lastBlock        = (file->offset + lastOffset) / tor->blockSize;
834    const uint64_t lastBlockOffset  = (file->offset + lastOffset) % tor->blockSize;
835    uint64_t haveBytes = 0;
836
837    assert( tor != NULL );
838    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
839    assert( file->offset + file->length <= tor->info.totalSize );
840    assert( ( (int)firstBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
841    assert( ( (int)lastBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
842    assert( firstBlock <= lastBlock );
843    assert( (int)tr_torBlockPiece( tor, firstBlock ) == file->firstPiece );
844    assert( (int)tr_torBlockPiece( tor, lastBlock ) == file->lastPiece );
845
846    if( firstBlock == lastBlock )
847    {
848        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
849            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
850    }
851    else
852    {
853        uint64_t i;
854
855        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
856            haveBytes += tor->blockSize - firstBlockOffset;
857
858        for( i=firstBlock+1; i<lastBlock; ++i )
859            if( tr_cpBlockIsComplete( tor->completion, i ) )
860               haveBytes += tor->blockSize;
861
862        if( tr_cpBlockIsComplete( tor->completion, lastBlock ) )
863            haveBytes += lastBlockOffset + 1;
864    }
865
866    return haveBytes;
867}
868
869tr_file_stat *
870tr_torrentFiles( const tr_torrent * tor, int * fileCount )
871{
872    int i;
873    const int n = tor->info.fileCount;
874    tr_file_stat * files = tr_new0( tr_file_stat, n );
875    tr_file_stat * walk = files;
876
877    for( i=0; i<n; ++i, ++walk )
878    {
879        const tr_file * file = tor->info.files + i;
880        cp_status_t cp;
881
882        walk->bytesCompleted = fileBytesCompleted( tor, i );
883
884        walk->progress = file->length
885            ? walk->bytesCompleted / (float)file->length
886            : 1.0;
887
888        if( walk->bytesCompleted >= file->length )
889            cp = TR_CP_COMPLETE;
890        else if( tor->info.files[i].dnd )
891            cp = TR_CP_DONE;
892        else
893            cp = TR_CP_INCOMPLETE;
894
895        walk->completionStatus = cp;
896    }
897
898    *fileCount = n;
899
900    return files;
901}
902
903void
904tr_torrentFilesFree( tr_file_stat * files, int fileCount UNUSED )
905{
906    tr_free( files );
907}
908
909/***
910****
911***/
912
913tr_peer_stat *
914tr_torrentPeers( const tr_torrent * tor, int * peerCount )
915{
916    return tr_peerMgrPeerStats( tor->handle->peerMgr,
917                                tor->info.hash, peerCount );
918}
919
920void tr_torrentPeersFree( tr_peer_stat * peers, int peerCount UNUSED )
921{
922    tr_free( peers );
923}
924
925void tr_torrentAvailability( const tr_torrent * tor, int8_t * tab, int size )
926{
927    return tr_peerMgrTorrentAvailability( tor->handle->peerMgr,
928                                          tor->info.hash,
929                                          tab, size );
930}
931
932void tr_torrentAmountFinished( const tr_torrent * tor, float * tab, int size )
933{
934    int i;
935    float interval;
936    tr_torrentLock( tor );
937
938    interval = (float)tor->info.pieceCount / (float)size;
939    for( i = 0; i < size; i++ )
940    {
941        int piece = i * interval;
942        tab[i] = tr_cpPercentBlocksInPiece( tor->completion, piece );
943    }
944
945    tr_torrentUnlock( tor );
946}
947
948void
949tr_torrentResetTransferStats( tr_torrent * tor )
950{
951    tr_torrentLock( tor );
952
953    tor->downloadedPrev += tor->downloadedCur;
954    tor->downloadedCur   = 0;
955    tor->uploadedPrev   += tor->uploadedCur;
956    tor->uploadedCur     = 0;
957    tor->corruptPrev    += tor->corruptCur;
958    tor->corruptCur      = 0;
959
960    tr_torrentUnlock( tor );
961}
962
963
964void
965tr_torrentSetHasPiece( tr_torrent * tor, int pieceIndex, int has )
966{
967    tr_torrentLock( tor );
968
969    if( has )
970        tr_cpPieceAdd( tor->completion, pieceIndex );
971    else
972        tr_cpPieceRem( tor->completion, pieceIndex );
973
974    tr_torrentUnlock( tor );
975}
976
977void tr_torrentRemoveSaved( tr_torrent * tor )
978{
979    tr_metainfoRemoveSaved( tor->info.hashString, tor->handle->tag );
980}
981
982void tr_torrentRecheck( tr_torrent * tor )
983{
984    if( !tor->uncheckedPieces )
985        tor->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
986    tr_bitfieldAddRange( tor->uncheckedPieces, 0, tor->info.pieceCount );
987
988    tr_ioRecheckAdd( tor, recheckDoneCB, tor->runStatus );
989}
990
991/***
992****
993***/
994
995void
996tr_torrentStart( tr_torrent * tor )
997{
998    tr_ioRecheckAdd( tor, recheckDoneCB, TR_RUN_RUNNING );
999}
1000
1001static void
1002tr_torrentFree( tr_torrent * tor )
1003{
1004    tr_torrent * t;
1005    tr_handle * h = tor->handle;
1006    tr_info * inf = &tor->info;
1007fprintf( stderr, "closing torrent %s\n", tor->info.name );
1008
1009    assert( tor != NULL );
1010    assert( tor->runStatus == TR_RUN_STOPPED );
1011
1012    tr_sharedLock( h->shared );
1013
1014    tr_lockFree( tor->lock );
1015    tr_cpClose( tor->completion );
1016
1017    tr_rcClose( tor->upload );
1018    tr_rcClose( tor->download );
1019    tr_rcClose( tor->swarmspeed );
1020
1021    tr_trackerUnsubscribe( tor->tracker, tor->trackerSubscription );
1022    tr_trackerFree( tor->tracker );
1023    tor->tracker = NULL;
1024
1025    tr_free( tor->destination );
1026
1027    if( tor == h->torrentList )
1028        h->torrentList = tor->next;
1029    else for( t=h->torrentList; t!=NULL; t=t->next ) {
1030        if( t->next == tor ) {
1031            t->next = tor->next;
1032            break;
1033        }
1034    }
1035
1036    assert( h->torrentCount >= 1 );
1037    h->torrentCount--;
1038
1039    tr_inf( "closing torrent %s... %d torrents left",
1040            tor->info.name, h->torrentCount );
1041
1042    tr_peerMgrRemoveTorrent( h->peerMgr, tor->info.hash );
1043
1044    tr_metainfoFree( inf );
1045    tr_free( tor );
1046
1047    tr_sharedUnlock( h->shared );
1048}
1049
1050static int
1051freeWhenStopped( void * vtor )
1052{
1053    tr_torrent * tor = vtor;
1054
1055    if( tor->runStatus != TR_RUN_STOPPED ) /* keep waiting */
1056        return TRUE;
1057
1058    tr_torrentFree( tor );
1059    return FALSE;
1060}
1061
1062static void
1063tr_torrentStopImpl( void * vtor )
1064{
1065    tr_torrent * tor = vtor;
1066
1067    switch( tor->runStatus )
1068    {
1069        case TR_RUN_CHECKING_WAIT:
1070        case TR_RUN_CHECKING:
1071            tr_ioRecheckRemove( tor );
1072            tr_torrentStop( tor );
1073            break;
1074
1075        case TR_RUN_RUNNING:
1076            saveFastResumeNow( tor );
1077            tr_peerMgrStopTorrent( tor->handle->peerMgr, tor->info.hash );
1078            tor->runStatus = TR_RUN_STOPPING;
1079            tor->stopDate = tr_date( );
1080            tr_trackerStop( tor->tracker );
1081            tr_ioClose( tor );
1082            break;
1083
1084        case TR_RUN_STOPPING:
1085        case TR_RUN_STOPPED:
1086            break;
1087    }
1088}
1089
1090void
1091tr_torrentStop( tr_torrent * tor )
1092{
1093    tr_runInEventThread( tor->handle, tr_torrentStopImpl, tor );
1094}
1095
1096void
1097tr_torrentClose( tr_torrent * tor )
1098{
1099    tor->runStatusToSave = tor->runStatus;
1100    tor->runStatusToSaveIsSet = TRUE;
1101    tor->dieFlag = TRUE;
1102    tr_torrentStop( tor );
1103    tr_timerNew( tor->handle, freeWhenStopped, tor, 250 );
1104}
1105
1106static void
1107recheckCpState( tr_torrent * tor )
1108{
1109    cp_status_t cpStatus;
1110
1111    tr_torrentLock( tor );
1112
1113    cpStatus = tr_cpGetStatus( tor->completion );
1114    if( cpStatus != tor->cpStatus ) {
1115        tor->cpStatus = cpStatus;
1116        tor->hasChangedState = tor->cpStatus;  /* tell the client... */
1117        if( (cpStatus == TR_CP_COMPLETE) /* ...and if we're complete */
1118            && tor->downloadedCur ) {        /* and it just happened */
1119            tr_trackerCompleted( tor->tracker ); /* tell the tracker */
1120        }
1121        tr_ioClose( tor );
1122        saveFastResumeSoon( tor );
1123    }
1124    tr_torrentUnlock( tor );
1125}
1126
1127/**
1128***  File priorities
1129**/
1130
1131void
1132tr_torrentSetFilePriority( tr_torrent   * tor,
1133                           int            fileIndex,
1134                           tr_priority_t  priority )
1135{
1136    int i;
1137    tr_file * file;
1138
1139    tr_torrentLock( tor );
1140
1141    assert( tor != NULL );
1142    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
1143    assert( priority==TR_PRI_LOW || priority==TR_PRI_NORMAL || priority==TR_PRI_HIGH );
1144
1145    file = &tor->info.files[fileIndex];
1146    file->priority = priority;
1147    for( i=file->firstPiece; i<=file->lastPiece; ++i )
1148      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
1149
1150    tr_dbg ( "Setting file #%d (pieces %d-%d) priority to %d (%s)",
1151             fileIndex, file->firstPiece, file->lastPiece,
1152             priority, tor->info.files[fileIndex].name );
1153
1154    saveFastResumeSoon( tor );
1155
1156    tr_torrentUnlock( tor );
1157}
1158
1159void
1160tr_torrentSetFilePriorities( tr_torrent     * tor,
1161                             int            * files,
1162                             int              fileCount,
1163                             tr_priority_t    priority )
1164{
1165    int i;
1166    for( i=0; i<fileCount; ++i )
1167        tr_torrentSetFilePriority( tor, files[i], priority );
1168}
1169
1170tr_priority_t
1171tr_torrentGetFilePriority( const tr_torrent *  tor, int file )
1172{
1173    tr_priority_t ret;
1174
1175    tr_torrentLock( tor );
1176    assert( tor != NULL );
1177    assert( 0<=file && file<tor->info.fileCount );
1178    ret = tor->info.files[file].priority;
1179    tr_torrentUnlock( tor );
1180
1181    return ret;
1182}
1183
1184
1185tr_priority_t*
1186tr_torrentGetFilePriorities( const tr_torrent * tor )
1187{
1188    int i;
1189    tr_priority_t * p;
1190
1191    tr_torrentLock( tor );
1192    p = tr_new0( tr_priority_t, tor->info.fileCount );
1193    for( i=0; i<tor->info.fileCount; ++i )
1194        p[i] = tor->info.files[i].priority;
1195    tr_torrentUnlock( tor );
1196
1197    return p;
1198}
1199
1200/**
1201***  File DND
1202**/
1203
1204int
1205tr_torrentGetFileDL( const tr_torrent * tor,
1206                     int                file )
1207{
1208    int doDownload;
1209    tr_torrentLock( tor );
1210
1211    assert( 0<=file && file<tor->info.fileCount );
1212    doDownload = !tor->info.files[file].dnd;
1213
1214    tr_torrentUnlock( tor );
1215    return doDownload != 0;
1216}
1217
1218void
1219tr_torrentSetFileDL( tr_torrent  * tor,
1220                     int           fileIndex,
1221                     int           doDownload )
1222{
1223    tr_file * file;
1224    const int dnd = !doDownload;
1225    int firstPiece, firstPieceDND;
1226    int lastPiece, lastPieceDND;
1227    int i;
1228
1229    tr_torrentLock( tor );
1230
1231    file = &tor->info.files[fileIndex];
1232    file->dnd = dnd;
1233    firstPiece = file->firstPiece;
1234    lastPiece = file->lastPiece;
1235
1236    /* can't set the first piece to DND unless
1237       every file using that piece is DND */
1238    firstPieceDND = dnd;
1239    for( i=fileIndex-1; firstPieceDND && i>=0; --i ) {
1240        if( tor->info.files[i].lastPiece != firstPiece )
1241            break;
1242        firstPieceDND = tor->info.files[i].dnd;
1243    }
1244
1245    /* can't set the last piece to DND unless
1246       every file using that piece is DND */
1247    lastPieceDND = dnd;
1248    for( i=fileIndex+1; lastPieceDND && i<tor->info.fileCount; ++i ) {
1249        if( tor->info.files[i].firstPiece != lastPiece )
1250            break;
1251        lastPieceDND = tor->info.files[i].dnd;
1252    }
1253
1254    if( firstPiece == lastPiece )
1255    {
1256        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
1257    }
1258    else
1259    {
1260        tor->info.pieces[firstPiece].dnd = firstPieceDND;
1261        tor->info.pieces[lastPiece].dnd = lastPieceDND;
1262        for( i=firstPiece+1; i<lastPiece; ++i )
1263            tor->info.pieces[i].dnd = dnd;
1264    }
1265
1266    tr_cpInvalidateDND ( tor->completion );
1267
1268    saveFastResumeSoon( tor );
1269
1270    tr_torrentUnlock( tor );
1271}
1272
1273void
1274tr_torrentSetFileDLs ( tr_torrent  * tor,
1275                       int         * files,
1276                       int           fileCount,
1277                       int           doDownload )
1278{
1279    int i;
1280    for( i=0; i<fileCount; ++i )
1281        tr_torrentSetFileDL( tor, files[i], doDownload );
1282}
1283
1284/***
1285****
1286***/
1287
1288int _tr_block( const tr_torrent * tor, int index, int begin )
1289{
1290    const tr_info * inf = &tor->info;
1291    return index * ( inf->pieceSize / tor->blockSize ) +
1292        begin / tor->blockSize;
1293}
Note: See TracBrowser for help on using the repository browser.