source: trunk/libtransmission/torrent.c @ 3119

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

slightly less broken handling of cases where we can't bind to our listening port.

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