source: trunk/libtransmission/torrent.c @ 2891

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

added support for `corrupt' announce tag s.t. corrupted bytes don't count towards your download ratio

  • Property svn:keywords set to Date Rev Author Id
File size: 38.6 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 2891 2007-08-21 15:17:02Z 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 "fastresume.h"
35#include "inout.h"
36#include "metainfo.h"
37#include "net.h" /* tr_netNtop */
38#include "peer.h"
39#include "platform.h"
40#include "ratecontrol.h"
41#include "shared.h"
42#include "tracker.h"
43#include "trcompat.h" /* for strlcpy */
44#include "utils.h"
45
46/***
47****  LOCKS
48***/
49
50void
51tr_torrentReaderLock( const tr_torrent_t * tor )
52{
53    tr_rwReaderLock ( (tr_rwlock_t*)tor->lock );
54}
55
56void
57tr_torrentReaderUnlock( const tr_torrent_t * tor )
58{
59    tr_rwReaderUnlock ( (tr_rwlock_t*)tor->lock );
60}
61
62void
63tr_torrentWriterLock( tr_torrent_t * tor )
64{
65    tr_rwWriterLock ( tor->lock );
66}
67
68void
69tr_torrentWriterUnlock( tr_torrent_t * tor )
70{
71    tr_rwWriterUnlock ( tor->lock );
72}
73
74/***
75****  PER-TORRENT UL / DL SPEEDS
76***/
77
78void
79tr_torrentSetSpeedMode( tr_torrent_t     * tor,
80                        int                up_or_down,
81                        tr_speedlimit_t    mode )
82{
83    tr_speedlimit_t * limit = up_or_down==TR_UP
84        ? &tor->uploadLimitMode
85        : &tor->downloadLimitMode;
86    *limit = mode;
87}
88
89tr_speedlimit_t
90tr_torrentGetSpeedMode( const tr_torrent_t * tor,
91                        int                  up_or_down)
92{
93    return up_or_down==TR_UP ? tor->uploadLimitMode
94                             : tor->downloadLimitMode;
95}
96
97void
98tr_torrentSetSpeedLimit( tr_torrent_t   * tor,
99                         int              up_or_down,
100                         int              single_KiB_sec )
101{
102    tr_ratecontrol_t * rc = up_or_down==TR_UP ? tor->upload : tor->download;
103    tr_rcSetLimit( rc, single_KiB_sec );
104}
105
106int
107tr_torrentGetSpeedLimit( const tr_torrent_t  * tor,
108                         int                   up_or_down )
109{
110    tr_ratecontrol_t * rc = up_or_down==TR_UP ? tor->upload : tor->download;
111    return tr_rcGetLimit( rc );
112}
113
114/***
115****
116***/
117
118static void setRunState( tr_torrent_t *, run_status_t );
119
120static void
121onTrackerResponse( void * tracker UNUSED, void * vevent, void * user_data )
122{
123    tr_torrent_t * tor = (tr_torrent_t *) user_data;
124    tr_tracker_event_t * event = (tr_tracker_event_t *) vevent;
125
126    switch( event->messageType )
127    {
128        case TR_TRACKER_PEERS:
129            tr_torrentAddCompact( tor, TR_PEER_FROM_TRACKER,
130                                  event->peerCompact, event->peerCount );
131            break;
132
133        case TR_TRACKER_WARNING:
134            tr_err( "Tracker: Warning - %s", event->text );
135            tor->error = TR_ERROR_TC_WARNING;
136            strlcpy( tor->errorString, event->text, sizeof(tor->errorString) );
137            break;
138
139        case TR_TRACKER_ERROR:
140            tr_err( "Tracker: Error - %s", event->text );
141            tor->error = TR_ERROR_TC_ERROR;
142            strlcpy( tor->errorString, event->text, sizeof(tor->errorString) );
143            break;
144
145        case TR_TRACKER_STOPPED:
146            if( tor->runStatus == TR_RUN_STOPPING_NET_WAIT )
147                setRunState( tor, TR_RUN_STOPPED );
148            break;
149    }
150}
151
152/***
153****
154****  TORRENT INSTANTIATION
155****
156***/
157
158static int
159getBytePiece( const tr_info_t * info, uint64_t byteOffset )
160{
161    assert( info != NULL );
162    assert( info->pieceSize != 0 );
163
164    return byteOffset / info->pieceSize;
165}
166
167static void
168initFilePieces ( tr_info_t * info, int fileIndex )
169{
170    tr_file_t * file = &info->files[fileIndex];
171    uint64_t firstByte, lastByte;
172
173    assert( info != NULL );
174    assert( 0<=fileIndex && fileIndex<info->fileCount );
175
176    file = &info->files[fileIndex];
177    firstByte = file->offset;
178    lastByte = firstByte + (file->length ? file->length-1 : 0);
179    file->firstPiece = getBytePiece( info, firstByte );
180    file->lastPiece = getBytePiece( info, lastByte );
181}
182
183static tr_priority_t
184calculatePiecePriority ( const tr_torrent_t * tor,
185                         int                  piece )
186{
187    int i;
188    tr_priority_t priority = TR_PRI_NORMAL;
189
190    for( i=0; i<tor->info.fileCount; ++i )
191    {
192        const tr_file_t * file = &tor->info.files[i];
193        if ( file->firstPiece <= piece
194          && file->lastPiece  >= piece
195          && file->priority   >  priority)
196              priority = file->priority;
197
198        /* when dealing with multimedia files, getting the first and
199           last pieces can sometimes allow you to preview it a bit
200           before it's fully downloaded... */
201        if ( file->priority >= TR_PRI_NORMAL )
202            if ( file->firstPiece == piece || file->lastPiece == piece )
203                priority = TR_PRI_HIGH;
204    }
205
206    return priority;
207}
208
209static void
210tr_torrentInitFilePieces( tr_torrent_t * tor )
211{
212    int i;
213    uint64_t offset = 0;
214
215    assert( tor != NULL );
216
217    for( i=0; i<tor->info.fileCount; ++i ) {
218      tor->info.files[i].offset = offset;
219      offset += tor->info.files[i].length;
220      initFilePieces( &tor->info, i );
221    }
222
223    for( i=0; i<tor->info.pieceCount; ++i )
224        tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
225}
226
227static void torrentThreadLoop( void * );
228
229static void
230torrentRealInit( tr_handle_t   * h,
231                 tr_torrent_t  * tor,
232                 const char    * destination,
233                 int             flags )
234{
235    uint64_t loaded;
236    uint64_t t;
237    char name[512];
238    tr_bitfield_t * uncheckedPieces;
239    tr_info_t * info = &tor->info;
240   
241    tor->info.flags |= flags;
242
243    tr_sharedLock( h->shared );
244
245    tor->destination = tr_strdup( destination );
246
247    tor->handle   = h;
248    tor->azId     = h->azId;
249    tor->hasChangedState = -1;
250    tor->pexDisabled = 0;
251
252    tor->runStatusToSaveIsSet = FALSE;
253
254    /**
255     * Decide on a block size.  constraints:
256     * (1) most clients decline requests over 16 KiB
257     * (2) pieceSize must be a multiple of block size
258     */
259    tor->blockSize = info->pieceSize;
260    while( tor->blockSize > (1024*16) )
261        tor->blockSize /= 2;
262
263    tor->lastPieceSize = info->totalSize % info->pieceSize;
264
265    if( !tor->lastPieceSize )
266         tor->lastPieceSize = info->pieceSize;
267
268    tor->lastBlockSize = info->totalSize % tor->blockSize;
269
270    if( !tor->lastBlockSize )
271         tor->lastBlockSize = tor->blockSize;
272
273    tor->blockCount =
274        ( info->totalSize + tor->blockSize - 1 ) / tor->blockSize;
275
276    tor->blockCountInPiece =
277        info->pieceSize / tor->blockSize;
278
279    tor->blockCountInLastPiece =
280        ( tor->lastPieceSize + tor->blockSize - 1 ) / tor->blockSize;
281
282    /* check our work */
283    assert( ( info->pieceSize % tor->blockSize ) == 0 );
284    t = info->pieceCount - 1;
285    t *= info->pieceSize;
286    t += tor->lastPieceSize;
287    assert( t == info->totalSize );
288    t = tor->blockCount - 1;
289    t *= tor->blockSize;
290    t += tor->lastBlockSize;
291    assert( t == info->totalSize );
292    t = info->pieceCount - 1;
293    t *= tor->blockCountInPiece;
294    t += tor->blockCountInLastPiece;
295    assert( t == (uint64_t)tor->blockCount );
296
297    tor->completion = tr_cpInit( tor );
298
299    tr_torrentInitFilePieces( tor );
300
301    tor->lock = tr_rwNew( );
302
303    tor->upload         = tr_rcInit();
304    tor->download       = tr_rcInit();
305    tor->swarmspeed     = tr_rcInit();
306 
307    /* We have a new torrent */
308    tor->publicPort = tr_sharedGetPublicPort( h->shared );
309
310    tr_sharedUnlock( h->shared );
311
312    if( !h->isPortSet )
313        tr_setBindPort( h, TR_DEFAULT_PORT );
314
315    assert( !tor->downloadedCur );
316    assert( !tor->uploadedCur );
317
318    tor->error   = TR_OK;
319
320    uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
321    loaded = tr_fastResumeLoad( tor, uncheckedPieces );
322
323    /* the `paused' flag has highest precedence...
324       after that, the fastresume setting is used...
325       if that's not found, default to RUNNING */
326    if( flags & TR_FLAG_PAUSED )
327        tor->runStatus = TR_RUN_STOPPED;
328    else if( !(loaded & TR_FR_RUN ) )
329        tor->runStatus = TR_RUN_RUNNING;
330
331    if( tr_bitfieldIsEmpty( uncheckedPieces ) )
332        tr_bitfieldFree( uncheckedPieces );
333    else
334        tor->uncheckedPieces = uncheckedPieces;
335
336
337    if( !(loaded & TR_FR_SPEEDLIMIT ) ) {
338        int limit, enabled;
339        tr_getGlobalSpeedLimit( tor->handle, TR_UP, &enabled, &limit );
340        tr_torrentSetSpeedLimit( tor, TR_UP, limit );
341        tr_getGlobalSpeedLimit( tor->handle, TR_DOWN, &enabled, &limit );
342        tr_torrentSetSpeedLimit( tor, TR_DOWN, limit );
343    }
344
345    tor->cpStatus = tr_cpGetStatus( tor->completion );
346
347    tor->tracker = tr_trackerNew( tor );
348    tor->trackerSubscription = tr_trackerSubscribe( tor->tracker, onTrackerResponse, tor );
349    if( tor->runStatus == TR_RUN_RUNNING )
350        tr_trackerStart( tor->tracker );
351
352    tr_sharedLock( h->shared );
353    tor->next = h->torrentList;
354    h->torrentList = tor;
355    h->torrentCount++;
356    tr_sharedUnlock( h->shared );
357
358    snprintf( name, sizeof( name ), "torrent %p (%s)", tor, tor->info.name );
359    tor->thread = tr_threadNew( torrentThreadLoop, tor, name );
360}
361
362static int
363pathIsInUse ( const tr_handle_t   * h,
364              const char          * destination,
365              const char          * name )
366{
367    const tr_torrent_t * tor;
368   
369    for( tor=h->torrentList; tor; tor=tor->next )
370        if( !strcmp( destination, tor->destination )
371         && !strcmp( name, tor->info.name ) )
372            return TRUE;
373
374    return FALSE;
375}
376
377static int
378hashExists( const tr_handle_t   * h,
379            const uint8_t       * hash )
380{
381    const tr_torrent_t * tor;
382
383    for( tor=h->torrentList; tor; tor=tor->next )
384        if( !memcmp( hash, tor->info.hash, SHA_DIGEST_LENGTH ) )
385            return TRUE;
386
387    return FALSE;
388}
389
390static int
391infoCanAdd( const tr_handle_t   * h,
392            const char          * destination,
393            const tr_info_t     * info )
394{
395    if( hashExists( h, info->hash ) )
396        return TR_EDUPLICATE;
397
398    if( destination && pathIsInUse( h, destination, info->name ) )
399        return TR_EDUPLICATE;
400
401    return TR_OK;
402}
403
404int
405tr_torrentParse( const tr_handle_t  * h,
406                 const char         * path,
407                 const char         * destination,
408                 tr_info_t          * setme_info )
409{
410    int ret, doFree;
411    tr_info_t tmp;
412
413    if( setme_info == NULL )
414        setme_info = &tmp;
415
416    memset( setme_info, 0, sizeof( tr_info_t ) );
417    ret = tr_metainfoParseFile( setme_info, h->tag, path, FALSE );
418    doFree = !ret && (setme_info == &tmp);
419
420    if( ret == TR_OK )
421        ret = infoCanAdd( h, destination, setme_info );
422
423    if( doFree )
424        tr_metainfoFree( &tmp );
425
426    return ret;
427}
428 
429tr_torrent_t *
430tr_torrentInit( tr_handle_t   * h,
431                const char    * path,
432                const char    * destination,
433                int             flags,
434                int           * error )
435{
436    int val;
437    int tmpError;
438    tr_torrent_t * tor = NULL;
439
440    if( !error )
441         error = &tmpError;
442
443    if(( val = tr_torrentParse( h, path, destination, NULL )))
444        *error = val;
445    else if(!(( tor = tr_new0( tr_torrent_t, 1 ))))
446        *error = TR_EOTHER;
447    else {
448        tr_metainfoParseFile( &tor->info, h->tag, path, TR_FLAG_SAVE & flags );
449        torrentRealInit( h, tor, destination, flags );
450    }
451
452    return tor;
453}
454
455int
456tr_torrentParseHash( const tr_handle_t  * h,
457                     const char         * hashStr,
458                     const char         * destination,
459                     tr_info_t          * setme_info )
460{
461    int ret, doFree;
462    tr_info_t tmp;
463
464    if( setme_info == NULL )
465        setme_info = &tmp;
466
467    memset( setme_info, 0, sizeof( tr_info_t ) );
468    ret = tr_metainfoParseHash( setme_info, h->tag, hashStr );
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_t *
481tr_torrentInitSaved( tr_handle_t    * h,
482                     const char     * hashStr,
483                     const char     * destination,
484                     int              flags,
485                     int            * error )
486{
487    int val;
488    int tmpError;
489    tr_torrent_t * tor = NULL;
490
491    if( !error )
492         error = &tmpError;
493
494    if(( val = tr_torrentParseHash( h, hashStr, destination, NULL )))
495        *error = val;
496    else if(!(( tor = tr_new0( tr_torrent_t, 1 ))))
497        *error = TR_EOTHER;
498    else {
499        tr_metainfoParseHash( &tor->info, h->tag, hashStr );
500        torrentRealInit( h, tor, destination, (TR_FLAG_SAVE|flags) );
501    }
502
503    return tor;
504}
505
506static int
507tr_torrentParseData( const tr_handle_t  * h,
508                     const uint8_t      * data,
509                     size_t               size,
510                     const char         * destination,
511                     tr_info_t          * setme_info )
512{
513    int ret, doFree;
514    tr_info_t tmp;
515
516    if( setme_info == NULL )
517        setme_info = &tmp;
518
519    memset( setme_info, 0, sizeof( tr_info_t ) );
520    ret = tr_metainfoParseData( setme_info, h->tag, data, size, FALSE );
521    doFree = !ret && (setme_info == &tmp);
522
523    if( ret == TR_OK )
524        ret = infoCanAdd( h, destination, setme_info );
525
526    if( doFree )
527        tr_metainfoFree( &tmp );
528
529    return ret;
530}
531
532tr_torrent_t *
533tr_torrentInitData( tr_handle_t    * h,
534                    const uint8_t  * data,
535                    size_t           size,
536                    const char     * destination,
537                    int              flags,
538                    int            * error )
539{
540    int val;
541    int tmpError;
542    tr_torrent_t * tor = NULL;
543
544    if( !error )
545         error = &tmpError;
546
547    if(( val = tr_torrentParseData( h, data, size, destination, NULL )))
548        *error = val;
549    else if(!(( tor = tr_new0( tr_torrent_t, 1 ))))
550        *error = TR_EOTHER;
551    else {
552        tr_metainfoParseData( &tor->info, h->tag, data, size, TR_FLAG_SAVE & flags );
553        torrentRealInit( h, tor, destination, flags );
554    }
555
556    return tor;
557}
558
559const tr_info_t *
560tr_torrentInfo( const tr_torrent_t * tor )
561{
562    return &tor->info;
563}
564
565/***
566****
567***/
568
569#if 0
570int tr_torrentScrape( tr_torrent_t * tor, int * s, int * l, int * d )
571{
572    return tr_trackerScrape( tor, s, l, d );
573}
574#endif
575
576static void
577fastResumeSave( tr_torrent_t * tor )
578{
579    tr_fastResumeSave( tor );
580    tor->fastResumeDirty = FALSE;
581}
582
583void
584tr_torrentSetFolder( tr_torrent_t * tor, const char * path )
585{
586    tr_free( tor->destination );
587    tor->destination = tr_strdup( path );
588    fastResumeSave( tor );
589}
590
591const char*
592tr_torrentGetFolder( const tr_torrent_t * tor )
593{
594    return tor->destination;
595}
596
597void
598tr_torrentChangeMyPort( tr_torrent_t * tor, int port )
599{
600    tr_torrentWriterLock( tor );
601
602    tor->publicPort = port;
603
604    if( tor->tracker )
605        tr_trackerChangeMyPort( tor->tracker );
606
607    tr_torrentWriterUnlock( tor );
608}
609
610/***********************************************************************
611 * torrentReallyStop
612 ***********************************************************************
613 * Joins the download thread and frees/closes everything related to it.
614 **********************************************************************/
615
616void tr_torrentDisablePex( tr_torrent_t * tor, int disable )
617{
618    tr_torrentWriterLock( tor );
619
620    if( ! ( TR_FLAG_PRIVATE & tor->info.flags ) )
621    {
622        if( tor->pexDisabled != disable )
623        {
624            int i;
625            tor->pexDisabled = disable;
626            for( i=0; i<tor->peerCount; ++i )
627                tr_peerSetPrivate( tor->peers[i], disable );
628        }
629    }
630
631    tr_torrentWriterUnlock( tor );
632}
633
634static int tr_didStateChangeTo ( tr_torrent_t * tor, int status )
635{
636    int ret;
637
638    tr_torrentWriterLock( tor );
639    if (( ret = tor->hasChangedState == status ))
640        tor->hasChangedState = -1;
641    tr_torrentWriterUnlock( tor );
642
643    return ret;
644}
645
646int tr_getIncomplete( tr_torrent_t * tor )
647{
648    return tr_didStateChangeTo( tor, TR_CP_INCOMPLETE );
649}
650int tr_getDone( tr_torrent_t * tor )
651{
652    return tr_didStateChangeTo( tor, TR_CP_DONE );
653}
654int tr_getComplete( tr_torrent_t * tor )
655{
656    return tr_didStateChangeTo( tor, TR_CP_COMPLETE );
657}
658
659void
660tr_manualUpdate( tr_torrent_t * tor )
661{
662    if( tor->runStatus == TR_RUN_RUNNING )
663        tr_trackerReannounce( tor->tracker );
664}
665int
666tr_torrentCanManualUpdate( const tr_torrent_t * tor )
667{
668    return ( tor != NULL )
669        && ( tor->runStatus == TR_RUN_RUNNING )
670        && ( tr_trackerCanManualAnnounce( tor->tracker ) );
671}
672
673const tr_stat_t *
674tr_torrentStat( tr_torrent_t * tor )
675{
676    tr_stat_t * s;
677    struct tr_tracker_s * tc;
678    int i;
679
680    tr_torrentReaderLock( tor );
681
682    tor->statCur = ( tor->statCur + 1 ) % 2;
683    s = &tor->stats[tor->statCur];
684
685    s->error  = tor->error;
686    memcpy( s->errorString, tor->errorString,
687            sizeof( s->errorString ) );
688
689    tc = tor->tracker;
690    s->tracker = tr_trackerGetAddress( tor->tracker );
691
692    /* peers... */
693    memset( s->peersFrom, 0, sizeof( s->peersFrom ) );
694    s->peersTotal          = tor->peerCount;
695    s->peersConnected      = 0;
696    s->peersSendingToUs    = 0;
697    s->peersGettingFromUs  = 0;
698
699    for( i=0; i<tor->peerCount; ++i )
700    {
701        const tr_peer_t * peer = tor->peers[i];
702
703        if( tr_peerIsConnected( peer ) )
704        {
705            ++s->peersConnected;
706            ++s->peersFrom[tr_peerIsFrom(peer)];
707
708            if( tr_peerDownloadRate( peer ) > 0.01 )
709                ++s->peersSendingToUs;
710
711            if( tr_peerUploadRate( peer ) > 0.01 )
712                ++s->peersGettingFromUs;
713        }
714    }
715
716    s->percentComplete = tr_cpPercentComplete ( tor->completion );
717
718    s->percentDone = tr_cpPercentDone( tor->completion );
719    s->left = tr_cpLeftUntilDone( tor->completion );
720
721    if( tor->uncheckedPieces )
722        s->status = tor->runStatus==TR_RUN_CHECKING
723            ? TR_STATUS_CHECK
724            : TR_STATUS_CHECK_WAIT;
725    else switch( tor->runStatus ) {
726        case TR_RUN_STOPPING: /* fallthrough */
727        case TR_RUN_STOPPING_NET_WAIT: s->status = TR_STATUS_STOPPING; break;
728        case TR_RUN_STOPPED: s->status = TR_STATUS_STOPPED; break;
729        case TR_RUN_CHECKING: s->status = TR_STATUS_CHECK; break;
730        case TR_RUN_RUNNING: switch( tor->cpStatus ) {
731            case TR_CP_INCOMPLETE: s->status = TR_STATUS_DOWNLOAD; break;
732            case TR_CP_DONE: s->status = TR_STATUS_DONE; break;
733            case TR_CP_COMPLETE: s->status = TR_STATUS_SEED; break;
734        }
735    }
736
737    s->recheckProgress = (tor->uncheckedPieces == NULL)
738        ? 0.0
739        : 1.0 - ((double)tr_bitfieldCountTrueBits(tor->uncheckedPieces) / tor->info.pieceCount);
740
741    s->cpStatus = tor->cpStatus;
742
743    /* tr_rcRate() doesn't make the difference between 'piece'
744       messages and other messages, which causes a non-zero
745       download rate even tough we are not downloading. So we
746       force it to zero not to confuse the user. */
747    s->rateDownload = tor->runStatus==TR_RUN_RUNNING
748        ? tr_rcRate( tor->download )
749        : 0.0;
750    s->rateUpload = tr_rcRate( tor->upload );
751   
752    tr_trackerGetCounts( tc,
753                         &s->completedFromTracker,
754                         &s->leechers, 
755                         &s->seeders );
756
757    s->swarmspeed = tr_rcRate( tor->swarmspeed );
758   
759    s->startDate = tor->startDate;
760    s->activityDate = tor->activityDate;
761
762    s->eta = s->rateDownload < 0.1
763        ? -1.0f
764        : (s->left / s->rateDownload / 1024.0);
765
766    s->corrupt         = tor->corruptCur    + tor->corruptPrev;
767    s->uploaded        = tor->uploadedCur   + tor->uploadedPrev;
768    s->downloaded      = tor->downloadedCur + tor->downloadedPrev;
769    s->downloadedValid = tr_cpDownloadedValid( tor->completion );
770   
771    s->ratio = s->downloaded || s->downloadedValid
772      ? (float)s->uploaded / (float)MAX(s->downloaded, s->downloadedValid)
773      : TR_RATIO_NA; 
774   
775    tr_torrentReaderUnlock( tor );
776
777    return s;
778}
779
780/***
781****
782***/
783
784static uint64_t
785fileBytesCompleted ( const tr_torrent_t * tor, int fileIndex )
786{
787    const tr_file_t * file     =  &tor->info.files[fileIndex];
788    const uint64_t firstBlock       =  file->offset / tor->blockSize;
789    const uint64_t firstBlockOffset =  file->offset % tor->blockSize;
790    const uint64_t lastOffset       =  file->length ? (file->length-1) : 0;
791    const uint64_t lastBlock        = (file->offset + lastOffset) / tor->blockSize;
792    const uint64_t lastBlockOffset  = (file->offset + lastOffset) % tor->blockSize;
793    uint64_t haveBytes = 0;
794
795    assert( tor != NULL );
796    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
797    assert( file->offset + file->length <= tor->info.totalSize );
798    assert( ( (int)firstBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
799    assert( ( (int)lastBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
800    assert( firstBlock <= lastBlock );
801    assert( (int)tr_torBlockPiece( tor, firstBlock ) == file->firstPiece );
802    assert( (int)tr_torBlockPiece( tor, lastBlock ) == file->lastPiece );
803
804    if( firstBlock == lastBlock )
805    {
806        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
807            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
808    }
809    else
810    {
811        uint64_t i;
812
813        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
814            haveBytes += tor->blockSize - firstBlockOffset;
815
816        for( i=firstBlock+1; i<lastBlock; ++i )
817            if( tr_cpBlockIsComplete( tor->completion, i ) )
818               haveBytes += tor->blockSize;
819
820        if( tr_cpBlockIsComplete( tor->completion, lastBlock ) )
821            haveBytes += lastBlockOffset + 1;
822    }
823
824    return haveBytes;
825}
826
827tr_file_stat_t *
828tr_torrentFiles( const tr_torrent_t * tor, int * fileCount )
829{
830    int i;
831    const int n = tor->info.fileCount;
832    tr_file_stat_t * files = tr_new0( tr_file_stat_t, n );
833    tr_file_stat_t * walk = files;
834
835    for( i=0; i<n; ++i, ++walk )
836    {
837        const tr_file_t * file = tor->info.files + i;
838        cp_status_t cp;
839
840        walk->bytesCompleted = fileBytesCompleted( tor, i );
841
842        walk->progress = file->length
843            ? walk->bytesCompleted / (float)file->length
844            : 1.0;
845
846        if( walk->bytesCompleted >= file->length )
847            cp = TR_CP_COMPLETE;
848        else if( tor->info.files[i].dnd )
849            cp = TR_CP_DONE;
850        else
851            cp = TR_CP_INCOMPLETE;
852
853        walk->completionStatus = cp;
854    }
855
856    *fileCount = n;
857
858    return files;
859}
860
861void
862tr_torrentFilesFree( tr_file_stat_t * files, int fileCount UNUSED )
863{
864    tr_free( files );
865}
866
867/***
868****
869***/
870
871tr_peer_stat_t *
872tr_torrentPeers( const tr_torrent_t * tor, int * peerCount )
873{
874    tr_peer_stat_t * peers;
875
876    tr_torrentReaderLock( tor );
877
878    *peerCount = tor->peerCount;
879   
880    peers = tr_new0( tr_peer_stat_t, tor->peerCount ); 
881    if (peers != NULL)
882    {
883        tr_peer_t * peer;
884        struct in_addr * addr;
885        int i;
886        for( i=0; i<tor->peerCount; ++i )
887        {
888            peer = tor->peers[i];
889           
890            addr = tr_peerAddress( peer );
891            if( NULL != addr )
892            {
893                tr_netNtop( addr, peers[i].addr,
894                           sizeof( peers[i].addr ) );
895            }
896           
897            peers[i].client           =  tr_peerClient( peer );
898            peers[i].isConnected      =  tr_peerIsConnected( peer );
899            peers[i].from             =  tr_peerIsFrom( peer );
900            peers[i].progress         =  tr_peerProgress( peer );
901            peers[i].port             =  tr_peerPort( peer );
902
903            peers[i].uploadToRate     =  tr_peerUploadRate( peer );
904            peers[i].downloadFromRate =  tr_peerDownloadRate( peer );
905           
906            peers[i].isDownloading    =  peers[i].uploadToRate > 0.01;
907            peers[i].isUploading      =  peers[i].downloadFromRate > 0.01;
908        }
909    }
910   
911    tr_torrentReaderUnlock( tor );
912   
913    return peers;
914}
915
916void tr_torrentPeersFree( tr_peer_stat_t * peers, int peerCount UNUSED )
917{
918    tr_free( peers );
919}
920
921void tr_torrentAvailability( const tr_torrent_t * tor, int8_t * tab, int size )
922{
923    int i, j, piece;
924    float interval;
925
926    tr_torrentReaderLock( tor );
927
928    interval = (float)tor->info.pieceCount / (float)size;
929    for( i = 0; i < size; i++ )
930    {
931        piece = i * interval;
932
933        if( tr_cpPieceIsComplete( tor->completion, piece ) )
934        {
935            tab[i] = -1;
936            continue;
937        }
938
939        tab[i] = 0;
940        for( j = 0; j < tor->peerCount; j++ )
941        {
942            if( tr_peerHasPiece( tor->peers[j], piece ) )
943            {
944                (tab[i])++;
945            }
946        }
947    }
948
949    tr_torrentReaderUnlock( tor );
950}
951
952void tr_torrentAmountFinished( const tr_torrent_t * tor, float * tab, int size )
953{
954    int i;
955    float interval;
956    tr_torrentReaderLock( tor );
957
958    interval = (float)tor->info.pieceCount / (float)size;
959    for( i = 0; i < size; i++ )
960    {
961        int piece = i * interval;
962        tab[i] = tr_cpPercentBlocksInPiece( tor->completion, piece );
963    }
964
965    tr_torrentReaderUnlock( tor );
966}
967
968void
969tr_torrentResetTransferStats( tr_torrent_t * tor )
970{
971    tr_torrentWriterLock( tor );
972
973    tor->downloadedPrev += tor->downloadedCur;
974    tor->downloadedCur   = 0;
975    tor->uploadedPrev   += tor->uploadedCur;
976    tor->uploadedCur     = 0;
977    tor->corruptPrev    += tor->corruptCur;
978    tor->corruptCur      = 0;
979
980    tr_torrentWriterUnlock( tor );
981}
982
983
984void
985tr_torrentSetHasPiece( tr_torrent_t * tor, int pieceIndex, int has )
986{
987    tr_torrentWriterLock( tor );
988
989    if( has )
990        tr_cpPieceAdd( tor->completion, pieceIndex );
991    else
992        tr_cpPieceRem( tor->completion, pieceIndex );
993
994    tr_torrentWriterUnlock( tor );
995}
996
997void tr_torrentRemoveSaved( tr_torrent_t * tor )
998{
999    tr_metainfoRemoveSaved( tor->info.hashString, tor->handle->tag );
1000}
1001
1002void tr_torrentRecheck( tr_torrent_t * tor )
1003{
1004    if( !tor->uncheckedPieces )
1005        tor->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
1006    tr_bitfieldAddRange( tor->uncheckedPieces, 0, tor->info.pieceCount );
1007}
1008
1009
1010int tr_torrentAttachPeer( tr_torrent_t * tor, tr_peer_t * peer )
1011{
1012    int i;
1013    tr_peer_t * otherPeer;
1014
1015    assert( tor != NULL );
1016    assert( peer != NULL );
1017
1018    if( tor->peerCount >= TR_MAX_PEER_COUNT )
1019    {
1020        tr_peerDestroy(  peer );
1021        return 0;
1022    }
1023
1024    /* Don't accept two connections from the same IP */
1025    for( i = 0; i < tor->peerCount; i++ )
1026    {
1027        otherPeer = tor->peers[i];
1028        if( !memcmp( tr_peerAddress( peer ), tr_peerAddress( otherPeer ), 4 ) )
1029        {
1030            tr_peerDestroy(  peer );
1031            return 0;
1032        }
1033    }
1034
1035    tr_peerSetPrivate( peer, tor->info.flags & TR_FLAG_PRIVATE ||
1036                       tor->pexDisabled );
1037    tr_peerSetTorrent( peer, tor );
1038    tor->peers[tor->peerCount++] = peer;
1039
1040    return 1;
1041}
1042
1043int tr_torrentAddCompact( tr_torrent_t * tor, int from,
1044                           const uint8_t * buf, int count )
1045{
1046    struct in_addr addr;
1047    tr_port_t port;
1048    int i, added;
1049    tr_peer_t * peer;
1050
1051    added = 0;
1052    for( i=0; i<count; ++i )
1053    {
1054        memcpy( &addr, buf, 4 ); buf += 4;
1055        memcpy( &port, buf, 2 ); buf += 2;
1056        peer = tr_peerInit( &addr, port, -1, from );
1057        added += tr_torrentAttachPeer( tor, peer );
1058    }
1059
1060    return added;
1061}
1062
1063/***
1064****
1065***/
1066
1067static void setRunState( tr_torrent_t * tor, run_status_t run )
1068{
1069    tr_torrentWriterLock( tor );
1070    tor->runStatus = run;
1071    tr_torrentWriterUnlock( tor );
1072}
1073
1074void tr_torrentStart( tr_torrent_t * tor )
1075{
1076    setRunState( tor, TR_RUN_RUNNING );
1077
1078    tr_trackerStart( tor->tracker );
1079}
1080
1081void tr_torrentStop( tr_torrent_t * tor )
1082{
1083    if((tor->runStatus != TR_RUN_STOPPING) && (tor->runStatus != TR_RUN_STOPPED) )
1084        setRunState( tor, TR_RUN_STOPPING );
1085}
1086
1087void tr_torrentClose( tr_torrent_t * tor )
1088{
1089    tor->runStatusToSave = tor->runStatus;
1090    tor->runStatusToSaveIsSet = TRUE;
1091    tr_torrentStop( tor );
1092    tor->dieFlag = TRUE;
1093}
1094
1095static void
1096tr_torrentFree( tr_torrent_t * tor )
1097{
1098    tr_torrent_t * t;
1099    tr_handle_t * h = tor->handle;
1100    tr_info_t * inf = &tor->info;
1101
1102    tr_sharedLock( h->shared );
1103
1104    tr_rwFree( tor->lock );
1105    tr_cpClose( tor->completion );
1106
1107    tr_rcClose( tor->upload );
1108    tr_rcClose( tor->download );
1109    tr_rcClose( tor->swarmspeed );
1110
1111    tr_trackerUnsubscribe( tor->tracker, tor->trackerSubscription );
1112    tr_trackerFree( tor->tracker );
1113    tor->tracker = NULL;
1114
1115    tr_free( tor->destination );
1116
1117    tr_metainfoFree( inf );
1118
1119    if( tor == h->torrentList )
1120        h->torrentList = tor->next;
1121    else for( t=h->torrentList; t!=NULL; t=t->next ) {
1122        if( t->next == tor ) {
1123            t->next = tor->next;
1124            break;
1125        }
1126    }
1127
1128    h->torrentCount--;
1129
1130    tr_inf( "closing torrent %s... %d torrents left",
1131            tor->info.name, h->torrentCount );
1132
1133    tr_free( tor );
1134
1135    tr_sharedUnlock( h->shared );
1136}
1137
1138static void
1139recheckCpState( tr_torrent_t * tor )
1140{
1141    cp_status_t cpStatus;
1142
1143    tr_torrentWriterLock( tor );
1144
1145    cpStatus = tr_cpGetStatus( tor->completion );
1146    if( cpStatus != tor->cpStatus ) {
1147        tor->cpStatus = cpStatus;
1148        tor->hasChangedState = tor->cpStatus;  /* tell the client... */
1149        if( (cpStatus == TR_CP_COMPLETE) /* ...and if we're complete */
1150            && tor->downloadedCur ) {        /* and it just happened */
1151            tr_trackerCompleted( tor->tracker ); /* tell the tracker */
1152        }
1153        tr_ioSync( tor->io );
1154        fastResumeSave( tor );
1155    }
1156    tr_torrentWriterUnlock( tor );
1157}
1158
1159static void
1160torrentThreadLoop ( void * _tor )
1161{
1162    static tr_lock_t * checkFilesLock = NULL;
1163    tr_torrent_t * tor = _tor;
1164
1165    /* create the check-files mutex */
1166    if( !checkFilesLock )
1167         checkFilesLock = tr_lockNew( );
1168
1169    /* loop until the torrent is being deleted */
1170    while( ! ( tor->dieFlag && (tor->runStatus == TR_RUN_STOPPED) ) )
1171    {
1172        /* sleep a little while */
1173        tr_wait( tor->runStatus == TR_RUN_STOPPED ? 1600 : 600 );
1174
1175        if( tor->fastResumeDirty )
1176        {
1177            fastResumeSave( tor );
1178            recheckCpState( tor );
1179        }
1180
1181        /* if we're stopping... */
1182        if( tor->runStatus == TR_RUN_STOPPING )
1183        {
1184            int i;
1185            tr_torrentWriterLock( tor );
1186
1187            /* close the IO */
1188            tr_ioClose( tor->io );
1189            tor->io = NULL;
1190            fastResumeSave( tor );
1191
1192            /* close the peers */
1193            for( i=0; i<tor->peerCount; ++i )
1194                tr_peerDestroy( tor->peers[i] );
1195            tor->peerCount = 0;
1196
1197            /* resest the transfer rates */
1198            tr_rcReset( tor->download );
1199            tr_rcReset( tor->upload );
1200            tr_rcReset( tor->swarmspeed );
1201
1202            /* tell the tracker we're stopping */
1203            tr_trackerStop( tor->tracker );
1204            tor->runStatus = TR_RUN_STOPPING_NET_WAIT;
1205            tor->stopDate = tr_date();
1206            tr_torrentWriterUnlock( tor );
1207        }
1208
1209        if( tor->runStatus == TR_RUN_STOPPING_NET_WAIT )
1210        {
1211#if 0
1212            tr_torrentWriterLock( tor );
1213            tor->runStatus = TR_RUN_STOPPED;
1214            tr_torrentWriterUnlock( tor );
1215#endif
1216            continue;
1217        }
1218
1219        /* do we need to check files? */
1220        if( tor->uncheckedPieces )
1221        {
1222            if( !tr_lockTryLock( checkFilesLock ) )
1223            {
1224                run_status_t realStatus;
1225
1226                tr_torrentWriterLock( tor );
1227                realStatus = tor->runStatus;
1228                tor->runStatus = TR_RUN_CHECKING;
1229                tr_torrentWriterUnlock( tor );
1230
1231                tr_ioCheckFiles( tor );
1232                setRunState( tor, realStatus );
1233
1234                tr_torrentWriterLock( tor );
1235                tor->cpStatus = tr_cpGetStatus( tor->completion );
1236                tr_torrentWriterUnlock( tor );
1237
1238                tr_lockUnlock( checkFilesLock );
1239            }
1240            continue;
1241        }
1242
1243        /* if we're paused or stopped, not much to do... */
1244        if( tor->runStatus == TR_RUN_STOPPED )
1245            continue;
1246
1247        /* ping our peers if we're running... */
1248        if( tor->runStatus == TR_RUN_RUNNING )
1249        {
1250            int i;
1251
1252            /* starting to run... */
1253            if( tor->io == NULL )
1254            {
1255                *tor->errorString = '\0';
1256                tr_torrentResetTransferStats( tor );
1257                tor->io = tr_ioNew( tor );
1258                tor->startDate = tr_date();
1259            }
1260
1261            /* refresh our completion state */
1262            recheckCpState( tor );
1263
1264            /* Shuffle peers */
1265            if ( tor->peerCount > 1 ) {
1266                tr_peer_t * tmp = tor->peers[0];
1267                memmove( tor->peers, tor->peers+1,
1268                        (tor->peerCount-1) * sizeof(void*) );
1269                tor->peers[tor->peerCount - 1] = tmp;
1270            }
1271
1272            /* receive/send messages */
1273            tr_torrentWriterLock( tor );
1274            for( i=0; i<tor->peerCount; ) {
1275                tr_peer_t * peer = tor->peers[i];
1276                int ret = tr_peerPulse( peer );
1277                if( ret & TR_ERROR_IO_MASK ) {
1278                    tr_err( "Fatal error, stopping download (%d)", ret );
1279                    tor->runStatus = TR_RUN_STOPPING;
1280                    tor->error = ret;
1281                    strlcpy( tor->errorString,
1282                             tr_errorString(ret),
1283                             sizeof(tor->errorString) );
1284                    break;
1285                }
1286                if( ret ) {
1287                    tr_peerDestroy( peer );
1288                    tor->peerCount--;
1289                    memmove( &tor->peers[i], &tor->peers[i+1],
1290                             (tor->peerCount-i)*sizeof(void*) );
1291                    continue;
1292                }
1293                i++;
1294            }
1295            tr_torrentWriterUnlock( tor );
1296        }
1297    }
1298
1299    tr_ioClose( tor->io );
1300    tr_torrentFree( tor );
1301}
1302
1303
1304/**
1305***  File priorities
1306**/
1307
1308void
1309tr_torrentSetFilePriority( tr_torrent_t   * tor,
1310                           int              fileIndex,
1311                           tr_priority_t    priority )
1312{
1313    int i;
1314    tr_file_t * file;
1315
1316    tr_torrentWriterLock( tor );
1317
1318    assert( tor != NULL );
1319    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
1320    assert( priority==TR_PRI_LOW || priority==TR_PRI_NORMAL || priority==TR_PRI_HIGH );
1321
1322    file = &tor->info.files[fileIndex];
1323    file->priority = priority;
1324    for( i=file->firstPiece; i<=file->lastPiece; ++i )
1325      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
1326
1327    tr_dbg ( "Setting file #%d (pieces %d-%d) priority to %d (%s)",
1328             fileIndex, file->firstPiece, file->lastPiece,
1329             priority, tor->info.files[fileIndex].name );
1330
1331    tor->fastResumeDirty = TRUE;
1332
1333    tr_torrentWriterUnlock( tor );
1334}
1335
1336void
1337tr_torrentSetFilePriorities( tr_torrent_t        * tor,
1338                             int                 * files,
1339                             int                   fileCount,
1340                             tr_priority_t         priority )
1341{
1342    int i;
1343    for( i=0; i<fileCount; ++i )
1344        tr_torrentSetFilePriority( tor, files[i], priority );
1345}
1346
1347tr_priority_t
1348tr_torrentGetFilePriority( const tr_torrent_t *  tor, int file )
1349{
1350    tr_priority_t ret;
1351
1352    tr_torrentReaderLock( tor );
1353    assert( tor != NULL );
1354    assert( 0<=file && file<tor->info.fileCount );
1355    ret = tor->info.files[file].priority;
1356    tr_torrentReaderUnlock( tor );
1357
1358    return ret;
1359}
1360
1361
1362tr_priority_t*
1363tr_torrentGetFilePriorities( const tr_torrent_t * tor )
1364{
1365    int i;
1366    tr_priority_t * p;
1367
1368    tr_torrentReaderLock( tor );
1369    p = tr_new0( tr_priority_t, tor->info.fileCount );
1370    for( i=0; i<tor->info.fileCount; ++i )
1371        p[i] = tor->info.files[i].priority;
1372    tr_torrentReaderUnlock( tor );
1373
1374    return p;
1375}
1376
1377/**
1378***  File DND
1379**/
1380
1381int
1382tr_torrentGetFileDL( const tr_torrent_t * tor,
1383                     int                  file )
1384{
1385    int doDownload;
1386    tr_torrentReaderLock( tor );
1387
1388    assert( 0<=file && file<tor->info.fileCount );
1389    doDownload = !tor->info.files[file].dnd;
1390
1391    tr_torrentReaderUnlock( tor );
1392    return doDownload != 0;
1393}
1394
1395void
1396tr_torrentSetFileDL( tr_torrent_t  * tor,
1397                     int             fileIndex,
1398                     int             doDownload )
1399{
1400    tr_file_t * file;
1401    const int dnd = !doDownload;
1402    int firstPiece, firstPieceDND;
1403    int lastPiece, lastPieceDND;
1404    int i;
1405
1406    tr_torrentWriterLock( tor );
1407
1408    file = &tor->info.files[fileIndex];
1409    file->dnd = dnd;
1410    firstPiece = file->firstPiece;
1411    lastPiece = file->lastPiece;
1412
1413    /* can't set the first piece to DND unless
1414       every file using that piece is DND */
1415    firstPieceDND = dnd;
1416    for( i=fileIndex-1; firstPieceDND && i>=0; --i ) {
1417        if( tor->info.files[i].lastPiece != firstPiece )
1418            break;
1419        firstPieceDND = tor->info.files[i].dnd;
1420    }
1421
1422    /* can't set the last piece to DND unless
1423       every file using that piece is DND */
1424    lastPieceDND = dnd;
1425    for( i=fileIndex+1; lastPieceDND && i<tor->info.fileCount; ++i ) {
1426        if( tor->info.files[i].firstPiece != lastPiece )
1427            break;
1428        lastPieceDND = tor->info.files[i].dnd;
1429    }
1430
1431    if( firstPiece == lastPiece )
1432    {
1433        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
1434    }
1435    else
1436    {
1437        tor->info.pieces[firstPiece].dnd = firstPieceDND;
1438        tor->info.pieces[lastPiece].dnd = lastPieceDND;
1439        for( i=firstPiece+1; i<lastPiece; ++i )
1440            tor->info.pieces[i].dnd = dnd;
1441    }
1442
1443    tr_cpInvalidateDND ( tor->completion );
1444
1445    tor->fastResumeDirty = TRUE;
1446
1447    tr_torrentWriterUnlock( tor );
1448}
1449
1450void
1451tr_torrentSetFileDLs ( tr_torrent_t   * tor,
1452                       int            * files,
1453                       int              fileCount,
1454                       int              doDownload )
1455{
1456    int i;
1457    for( i=0; i<fileCount; ++i )
1458        tr_torrentSetFileDL( tor, files[i], doDownload );
1459}
1460
1461/***
1462****
1463***/
1464
1465int _tr_block( const tr_torrent_t * tor, int index, int begin )
1466{
1467    const tr_info_t * inf = &tor->info;
1468    return index * ( inf->pieceSize / tor->blockSize ) +
1469        begin / tor->blockSize;
1470}
Note: See TracBrowser for help on using the repository browser.