source: trunk/libtransmission/torrent.c @ 2317

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

minor cleanups of last checkin

  • Property svn:keywords set to Date Rev Author Id
File size: 32.2 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 2317 2007-07-10 03:23:07Z 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 "transmission.h"
26#include "fastresume.h"
27#include "trcompat.h" /* for strlcpy */
28#include "metainfo.h"
29#include "net.h" /* tr_netNtop */
30#include "shared.h"
31
32/***
33****  LOCKS
34***/
35
36void
37tr_torrentReaderLock( const tr_torrent_t * tor )
38{
39    tr_rwReaderLock ( (tr_rwlock_t*)&tor->lock );
40}
41
42void
43tr_torrentReaderUnlock( const tr_torrent_t * tor )
44{
45    tr_rwReaderUnlock ( (tr_rwlock_t*)&tor->lock );
46}
47
48void
49tr_torrentWriterLock( tr_torrent_t * tor )
50{
51    tr_rwWriterLock ( &tor->lock );
52}
53
54void
55tr_torrentWriterUnlock( tr_torrent_t * tor )
56{
57    tr_rwWriterUnlock ( &tor->lock );
58}
59
60/***
61****  PER-TORRENT UL / DL SPEEDS
62***/
63
64void
65tr_setUseCustomUpload( tr_torrent_t * tor, int limit )
66{
67    tr_torrentWriterLock( tor );
68    tor->customUploadLimit = limit;
69    tr_torrentWriterUnlock( tor );
70}
71
72void
73tr_setUseCustomDownload( tr_torrent_t * tor, int limit )
74{
75    tr_torrentWriterLock( tor );
76    tor->customDownloadLimit = limit;
77    tr_torrentWriterUnlock( tor );
78}
79
80void
81tr_setUploadLimit( tr_torrent_t * tor, int limit )
82{
83    tr_torrentWriterLock( tor );
84    tr_rcSetLimit( tor->upload, limit );
85    tr_torrentWriterUnlock( tor );
86}
87
88void
89tr_setDownloadLimit( tr_torrent_t * tor, int limit )
90{
91    tr_torrentWriterLock( tor );
92    tr_rcSetLimit( tor->download, limit );
93    tr_torrentWriterUnlock( tor );
94}
95
96/***
97****
98****  TORRENT INSTANTIATION
99****
100***/
101
102static int
103getBytePiece( const tr_info_t * info, uint64_t byteOffset )
104{
105    assert( info != NULL );
106    assert( info->pieceSize != 0 );
107
108    return byteOffset / info->pieceSize;
109}
110
111static void
112initFilePieces ( tr_info_t * info, int fileIndex )
113{
114    tr_file_t * file = &info->files[fileIndex];
115    uint64_t firstByte, lastByte;
116
117    assert( info != NULL );
118    assert( 0<=fileIndex && fileIndex<info->fileCount );
119
120    file = &info->files[fileIndex];
121    firstByte = file->offset;
122    lastByte = firstByte + (file->length ? file->length-1 : 0);
123    file->firstPiece = getBytePiece( info, firstByte );
124    file->lastPiece = getBytePiece( info, lastByte );
125    tr_dbg( "file #%d is in pieces [%d...%d] (%s)", fileIndex, file->firstPiece, file->lastPiece, file->name );
126}
127
128static tr_priority_t
129calculatePiecePriority ( const tr_torrent_t * tor,
130                         int                  piece )
131{
132    int i;
133    tr_priority_t priority = TR_PRI_NORMAL;
134
135    for( i=0; i<tor->info.fileCount; ++i )
136    {
137        const tr_file_t * file = &tor->info.files[i];
138        if ( file->firstPiece <= piece
139          && file->lastPiece  >= piece
140          && file->priority   >  priority)
141              priority = file->priority;
142    }
143
144    return priority;
145}
146
147static void
148tr_torrentInitFilePieces( tr_torrent_t * tor )
149{
150    int i;
151    uint64_t offset = 0;
152
153    assert( tor != NULL );
154
155    for( i=0; i<tor->info.fileCount; ++i ) {
156      tor->info.files[i].offset = offset;
157      offset += tor->info.files[i].length;
158      initFilePieces( &tor->info, i );
159    }
160
161    for( i=0; i<tor->info.pieceCount; ++i )
162      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
163}
164
165static void torrentThreadLoop( void * );
166
167static void
168torrentRealInit( tr_handle_t   * h,
169                 tr_torrent_t  * tor,
170                 const char    * destination,
171                 int             flags )
172{
173    int i;
174    char name[512];
175   
176    tor->info.flags |= flags;
177
178    tr_sharedLock( h->shared );
179
180    tor->destination = tr_strdup( destination );
181
182    tr_torrentInitFilePieces( tor );
183
184    tor->handle   = h;
185    tor->id       = h->id;
186    tor->key      = h->key;
187    tor->azId     = h->azId;
188    tor->hasChangedState = -1;
189   
190    /* Escaped info hash for HTTP queries */
191    for( i = 0; i < SHA_DIGEST_LENGTH; i++ )
192    {
193        snprintf( &tor->escapedHashString[3*i],
194                  sizeof( tor->escapedHashString ) - 3 * i,
195                  "%%%02x", tor->info.hash[i] );
196    }
197
198    tor->pexDisabled = 0;
199
200    /* Block size: usually 16 ko, or less if we have to */
201    tor->blockSize  = MIN( tor->info.pieceSize, 1 << 14 );
202    tor->blockCount = ( tor->info.totalSize + tor->blockSize - 1 ) /
203                        tor->blockSize;
204    tor->completion = tr_cpInit( tor );
205
206    tor->thread = THREAD_EMPTY;
207    tr_rwInit( &tor->lock );
208
209    tor->upload         = tr_rcInit();
210    tor->download       = tr_rcInit();
211    tor->swarmspeed     = tr_rcInit();
212 
213    /* We have a new torrent */
214    tor->publicPort = tr_sharedGetPublicPort( h->shared );
215
216    tr_sharedUnlock( h->shared );
217
218    if( !h->isPortSet )
219        tr_setBindPort( h, TR_DEFAULT_PORT );
220
221    assert( !tor->downloadedCur );
222    assert( !tor->uploadedCur );
223
224    tor->error   = TR_OK;
225    tor->runStatus = flags & TR_FLAG_PAUSED ? TR_RUN_STOPPED : TR_RUN_RUNNING;
226    tor->recheckFlag = tr_ioCheckFiles( tor, TR_RECHECK_FAST );
227    tor->cpStatus = tr_cpGetStatus( tor->completion );
228
229    tr_sharedLock( h->shared );
230    tor->next = h->torrentList;
231    h->torrentList = tor;
232    h->torrentCount++;
233    tr_sharedUnlock( h->shared );
234
235    snprintf( name, sizeof( name ), "torrent %p (%s)", tor, tor->info.name );
236    tr_threadCreate( &tor->thread, torrentThreadLoop, tor, name );
237}
238
239static int
240pathIsInUse ( const tr_handle_t   * h,
241              const char          * destination,
242              const char          * name )
243{
244    const tr_torrent_t * tor;
245   
246    for( tor=h->torrentList; tor; tor=tor->next )
247        if( !strcmp( destination, tor->destination )
248         && !strcmp( name, tor->info.name ) )
249            return TRUE;
250
251    return FALSE;
252}
253
254static int
255hashExists( const tr_handle_t   * h,
256            const uint8_t       * hash )
257{
258    const tr_torrent_t * tor;
259
260    for( tor=h->torrentList; tor; tor=tor->next )
261        if( !memcmp( hash, tor->info.hash, SHA_DIGEST_LENGTH ) )
262            return TRUE;
263
264    return FALSE;
265}
266
267static int
268infoCanAdd( const tr_handle_t   * h,
269            const char          * destination,
270            const tr_info_t     * info )
271{
272    if( hashExists( h, info->hash ) )
273        return TR_EDUPLICATE;
274
275    if( destination && pathIsInUse( h, destination, info->name ) )
276        return TR_EDUPLICATE;
277
278    return TR_OK;
279}
280
281int
282tr_torrentParse( const tr_handle_t  * h,
283                 const char         * path,
284                 const char         * destination,
285                 tr_info_t          * setme_info )
286{
287    int ret;
288    tr_info_t tmp;
289
290    if( setme_info == NULL )
291        setme_info = &tmp;
292
293    memset( setme_info, 0, sizeof( tr_info_t ) );
294    ret = tr_metainfoParseFile( setme_info, h->tag, path, FALSE );
295
296    if( ret == TR_OK )
297        ret = infoCanAdd( h, destination, setme_info );
298
299    if( setme_info == &tmp )
300        tr_metainfoFree( &tmp );
301
302    return ret;
303}
304 
305tr_torrent_t *
306tr_torrentInit( tr_handle_t   * h,
307                const char    * path,
308                const char    * destination,
309                int             flags,
310                int           * error )
311{
312    int val;
313    tr_torrent_t * tor = NULL;
314
315    if(( val = tr_torrentParse( h, path, destination, NULL )))
316        *error = val;
317    else if(!(( tor = tr_new0( tr_torrent_t, 1 ))))
318        *error = TR_EOTHER;
319    else {
320        tr_metainfoParseFile( &tor->info, h->tag, path, TR_FLAG_SAVE & flags );
321        torrentRealInit( h, tor, destination, flags );
322    }
323
324    return tor;
325}
326
327int
328tr_torrentParseHash( const tr_handle_t  * h,
329                     const char         * hashStr,
330                     const char         * destination,
331                     tr_info_t          * setme_info )
332{
333    int ret;
334    tr_info_t tmp;
335
336    if( setme_info == NULL )
337        setme_info = &tmp;
338
339    memset( setme_info, 0, sizeof( tr_info_t ) );
340    ret = tr_metainfoParseHash( setme_info, h->tag, hashStr );
341
342    if( ret == TR_OK )
343        ret = infoCanAdd( h, destination, setme_info );
344
345    if( setme_info == &tmp )
346        tr_metainfoFree( &tmp );
347
348    return ret;
349}
350
351
352tr_torrent_t *
353tr_torrentInitSaved( tr_handle_t    * h,
354                     const char     * hashStr,
355                     const char     * destination,
356                     int              flags,
357                     int            * error )
358{
359    int val;
360    tr_torrent_t * tor = NULL;
361
362    if(( val = tr_torrentParseHash( h, hashStr, destination, NULL )))
363        *error = val;
364    else if(!(( tor = tr_new0( tr_torrent_t, 1 ))))
365        *error = TR_EOTHER;
366    else {
367        tr_metainfoParseHash( &tor->info, h->tag, hashStr );
368        torrentRealInit( h, tor, destination, (TR_FLAG_SAVE|flags) );
369    }
370
371    return tor;
372}
373
374static int
375tr_torrentParseData( const tr_handle_t  * h,
376                     const uint8_t      * data,
377                     size_t               size,
378                     const char         * destination,
379                     tr_info_t          * setme_info )
380{
381    int ret;
382    tr_info_t tmp;
383
384    if( setme_info == NULL )
385        setme_info = &tmp;
386
387    memset( setme_info, 0, sizeof( tr_info_t ) );
388    ret = tr_metainfoParseData( setme_info, h->tag, data, size, FALSE );
389
390    if( ret == TR_OK )
391        ret = infoCanAdd( h, destination, setme_info );
392
393    if( setme_info == &tmp )
394        tr_metainfoFree( &tmp );
395
396    return ret;
397}
398
399tr_torrent_t *
400tr_torrentInitData( tr_handle_t    * h,
401                    const uint8_t  * data,
402                    size_t           size,
403                    const char     * destination,
404                    int              flags,
405                    int            * error )
406{
407    int val;
408    tr_torrent_t * tor = NULL;
409
410    if(( val = tr_torrentParseData( h, data, size, destination, NULL )))
411        *error = val;
412    else if(!(( tor = tr_new0( tr_torrent_t, 1 ))))
413        *error = TR_EOTHER;
414    else {
415        tr_metainfoParseData( &tor->info, h->tag, data, size, TR_FLAG_SAVE & flags );
416        torrentRealInit( h, tor, destination, flags );
417    }
418
419    return tor;
420}
421
422const tr_info_t *
423tr_torrentInfo( const tr_torrent_t * tor )
424{
425    return &tor->info;
426}
427
428/***
429****
430***/
431
432int tr_torrentScrape( tr_torrent_t * tor, int * s, int * l, int * d )
433{
434    return tr_trackerScrape( tor, s, l, d );
435}
436
437void tr_torrentSetFolder( tr_torrent_t * tor, const char * path )
438{
439    tr_free( tor->destination );
440    tor->destination = tr_strdup( path );
441
442    if( !tor->ioLoaded )
443         tor->ioLoaded = tr_ioLoadResume( tor ) == TR_OK;
444}
445
446const char* tr_torrentGetFolder( const tr_torrent_t * tor )
447{
448    return tor->destination;
449}
450
451
452/***********************************************************************
453 * torrentReallyStop
454 ***********************************************************************
455 * Joins the download thread and frees/closes everything related to it.
456 **********************************************************************/
457
458void tr_torrentDisablePex( tr_torrent_t * tor, int disable )
459{
460    tr_torrentWriterLock( tor );
461
462    if( ! ( TR_FLAG_PRIVATE & tor->info.flags ) )
463    {
464        if( tor->pexDisabled != disable )
465        {
466            int i;
467            tor->pexDisabled = disable;
468            for( i=0; i<tor->peerCount; ++i )
469                tr_peerSetPrivate( tor->peers[i], disable );
470        }
471    }
472
473    tr_torrentWriterUnlock( tor );
474}
475
476static int tr_didStateChangeTo ( tr_torrent_t * tor, int status )
477{
478    int ret;
479
480    tr_torrentWriterLock( tor );
481    if (( ret = tor->hasChangedState == status ))
482        tor->hasChangedState = -1;
483    tr_torrentWriterUnlock( tor );
484
485    return ret;
486}
487
488int tr_getIncomplete( tr_torrent_t * tor )
489{
490    return tr_didStateChangeTo( tor, TR_CP_INCOMPLETE );
491}
492int tr_getDone( tr_torrent_t * tor )
493{
494    return tr_didStateChangeTo( tor, TR_CP_DONE );
495}
496int tr_getComplete( tr_torrent_t * tor )
497{
498    return tr_didStateChangeTo( tor, TR_CP_COMPLETE );
499}
500
501void tr_manualUpdate( tr_torrent_t * tor UNUSED )
502{
503#if 0
504    int peerCount, new;
505    uint8_t * peerCompact;
506
507    if( tor->status != TR_RUN_RUNNING )
508        return;
509   
510    tr_torrentWriterLock( tor );
511    tr_trackerAnnouncePulse( tor->tracker, &peerCount, &peerCompact, 1 );
512    new = 0;
513    if( peerCount > 0 )
514    {
515        new = tr_torrentAddCompact( tor, TR_PEER_FROM_TRACKER,
516                                    peerCompact, peerCount );
517        free( peerCompact );
518    }
519    tr_dbg( "got %i peers from manual announce, used %i", peerCount, new );
520    tr_torrentWriterUnlock( tor );
521#endif
522}
523
524tr_stat_t * tr_torrentStat( tr_torrent_t * tor )
525{
526    tr_stat_t * s;
527    tr_tracker_t * tc;
528    int i;
529
530    tr_torrentReaderLock( tor );
531
532    tor->statCur = ( tor->statCur + 1 ) % 2;
533    s = &tor->stats[tor->statCur];
534
535    s->error  = tor->error;
536    memcpy( s->errorString, tor->errorString,
537            sizeof( s->errorString ) );
538
539    tc = tor->tracker;
540    s->cannotConnect = tr_trackerCannotConnect( tc );
541    s->tracker = tc
542        ? tr_trackerGet( tc )
543        : &tor->info.trackerList[0].list[0];
544
545    /* peers... */
546    memset( s->peersFrom, 0, sizeof( s->peersFrom ) );
547    s->peersTotal       = 0;
548    s->peersUploading   = 0;
549    s->peersDownloading = 0;
550    for( i=0; i<tor->peerCount; ++i ) {
551        const tr_peer_t * peer = tor->peers[i];
552            ++s->peersTotal;
553        if( tr_peerIsConnected( peer ) ) {
554            ++s->peersFrom[tr_peerIsFrom(peer)];
555            if( tr_peerAmInterested( peer ) && !tr_peerIsChoking( peer ) )
556                ++s->peersUploading;
557            if( !tr_peerAmChoking( peer ) )
558                ++s->peersDownloading;
559        }
560    }
561
562    s->percentDone = tr_cpPercentDone( tor->completion );
563    s->percentComplete = tr_cpPercentComplete( tor->completion );
564    s->left     = tr_cpLeftUntilDone( tor->completion );
565
566
567    if( tor->recheckFlag )
568        s->status = TR_STATUS_CHECK_WAIT;
569    else switch( tor->runStatus ) {
570        case TR_RUN_STOPPING: s->status = TR_STATUS_STOPPING; break;
571        case TR_RUN_STOPPED: s->status = TR_STATUS_STOPPED; break;
572        case TR_RUN_CHECKING: s->status = TR_STATUS_CHECK; break;
573        case TR_RUN_RUNNING: switch( tor->cpStatus ) {
574            case TR_CP_INCOMPLETE: s->status = TR_STATUS_DOWNLOAD; break;
575            case TR_CP_DONE: s->status = TR_STATUS_DONE; break;
576            case TR_CP_COMPLETE: s->status = TR_STATUS_SEED; break;
577        }
578    }
579
580    s->cpStatus = tor->cpStatus;
581
582    /* tr_rcRate() doesn't make the difference between 'piece'
583       messages and other messages, which causes a non-zero
584       download rate even tough we are not downloading. So we
585       force it to zero not to confuse the user. */
586    s->rateDownload = tor->runStatus==TR_RUN_RUNNING
587        ? tr_rcRate( tor->download )
588        : 0.0;
589    s->rateUpload = tr_rcRate( tor->upload );
590   
591    s->seeders  = tr_trackerSeeders( tc );
592    s->leechers = tr_trackerLeechers( tc );
593    s->completedFromTracker = tr_trackerDownloaded( tc );
594
595    s->swarmspeed = tr_rcRate( tor->swarmspeed );
596   
597    s->startDate = tor->startDate;
598    s->activityDate = tor->activityDate;
599
600    s->eta = s->rateDownload < 0.1
601        ? -1.0f
602        : (s->left / s->rateDownload / 1024.0);
603
604    s->uploaded        = tor->uploadedCur   + tor->uploadedPrev;
605    s->downloaded      = tor->downloadedCur + tor->downloadedPrev;
606    s->downloadedValid = tr_cpDownloadedValid( tor->completion );
607   
608    s->ratio = s->downloaded || s->downloadedValid
609      ? (float)s->uploaded / (float)MAX(s->downloaded, s->downloadedValid)
610      : TR_RATIO_NA; 
611   
612    tr_torrentReaderUnlock( tor );
613
614    return s;
615}
616
617tr_peer_stat_t *
618tr_torrentPeers( const tr_torrent_t * tor, int * peerCount )
619{
620    tr_peer_stat_t * peers;
621
622    tr_torrentReaderLock( tor );
623
624    *peerCount = tor->peerCount;
625   
626    peers = tr_new0( tr_peer_stat_t, tor->peerCount ); 
627    if (peers != NULL)
628    {
629        tr_peer_t * peer;
630        struct in_addr * addr;
631        int i;
632        for( i = 0; i < tor->peerCount; i++ )
633        {
634            peer = tor->peers[i];
635           
636            addr = tr_peerAddress( peer );
637            if( NULL != addr )
638            {
639                tr_netNtop( addr, peers[i].addr,
640                           sizeof( peers[i].addr ) );
641            }
642           
643            peers[i].client        = tr_peerClient( peer );
644            peers[i].isConnected   = tr_peerIsConnected( peer );
645            peers[i].from          = tr_peerIsFrom( peer );
646            peers[i].progress      = tr_peerProgress( peer );
647            peers[i].port          = tr_peerPort( peer );
648           
649            if( ( peers[i].isDownloading = !tr_peerAmChoking( peer ) ) )
650            {
651                peers[i].uploadToRate = tr_peerUploadRate( peer );
652            }
653            if( ( peers[i].isUploading = ( tr_peerAmInterested( peer ) &&
654                                           !tr_peerIsChoking( peer ) ) ) )
655            {
656                peers[i].downloadFromRate = tr_peerDownloadRate( peer );
657            }
658        }
659    }
660   
661    tr_torrentReaderUnlock( tor );
662   
663    return peers;
664}
665
666void tr_torrentPeersFree( tr_peer_stat_t * peers, int peerCount UNUSED )
667{
668    tr_free( peers );
669}
670
671void tr_torrentAvailability( const tr_torrent_t * tor, int8_t * tab, int size )
672{
673    int i, j, piece;
674    float interval;
675
676    tr_torrentReaderLock( tor );
677
678    interval = (float)tor->info.pieceCount / (float)size;
679    for( i = 0; i < size; i++ )
680    {
681        piece = i * interval;
682
683        if( tr_cpPieceIsComplete( tor->completion, piece ) )
684        {
685            tab[i] = -1;
686            continue;
687        }
688
689        tab[i] = 0;
690        for( j = 0; j < tor->peerCount; j++ )
691        {
692            if( tr_peerHasPiece( tor->peers[j], piece ) )
693            {
694                (tab[i])++;
695            }
696        }
697    }
698
699    tr_torrentReaderUnlock( tor );
700}
701
702uint64_t
703tr_torrentFileBytesCompleted ( const tr_torrent_t * tor, int fileIndex )
704{
705    const tr_file_t * file     =  &tor->info.files[fileIndex];
706    const uint64_t firstBlock       =  file->offset / tor->blockSize;
707    const uint64_t firstBlockOffset =  file->offset % tor->blockSize;
708    const uint64_t lastOffset       =  file->length ? (file->length-1) : 0;
709    const uint64_t lastBlock        = (file->offset + lastOffset) / tor->blockSize;
710    const uint64_t lastBlockOffset  = (file->offset + lastOffset) % tor->blockSize;
711    uint64_t haveBytes = 0;
712
713    assert( tor != NULL );
714    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
715    assert( file->offset + file->length <= tor->info.totalSize );
716    assert( (int)firstBlock < tor->blockCount );
717    assert( (int)lastBlock < tor->blockCount );
718    assert( firstBlock <= lastBlock );
719    assert( tr_blockPiece( firstBlock ) == file->firstPiece );
720    assert( tr_blockPiece( lastBlock ) == file->lastPiece );
721
722    if( firstBlock == lastBlock )
723    {
724        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
725            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
726    }
727    else
728    {
729        uint64_t i;
730
731        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
732            haveBytes += tor->blockSize - firstBlockOffset;
733
734        for( i=firstBlock+1; i<lastBlock; ++i )
735            if( tr_cpBlockIsComplete( tor->completion, i ) )
736               haveBytes += tor->blockSize;
737
738        if( tr_cpBlockIsComplete( tor->completion, lastBlock ) )
739            haveBytes += lastBlockOffset + 1;
740    }
741
742    return haveBytes;
743}
744
745float
746tr_torrentFileCompletion ( const tr_torrent_t * tor, int fileIndex )
747{
748    const uint64_t c = tr_torrentFileBytesCompleted ( tor, fileIndex );
749    uint64_t length = tor->info.files[fileIndex].length;
750   
751    if( !length )
752        return 1.0;
753    return (double)c / length;
754}
755
756float*
757tr_torrentCompletion( const tr_torrent_t * tor )
758{
759    int i;
760    float * f;
761    tr_torrentReaderLock( tor );
762
763    f = tr_new0( float, tor->info.fileCount );
764    for( i=0; i<tor->info.fileCount; ++i )
765       f[i] = tr_torrentFileCompletion ( tor, i );
766
767    tr_torrentReaderUnlock( tor );
768    return f;
769}
770
771void tr_torrentAmountFinished( const tr_torrent_t * tor, float * tab, int size )
772{
773    int i;
774    float interval;
775    tr_torrentReaderLock( tor );
776
777    interval = (float)tor->info.pieceCount / (float)size;
778    for( i = 0; i < size; i++ )
779    {
780        int piece = i * interval;
781        tab[i] = tr_cpPercentBlocksInPiece( tor->completion, piece );
782    }
783
784    tr_torrentReaderUnlock( tor );
785}
786
787void
788tr_torrentResetTransferStats( tr_torrent_t * tor )
789{
790    tr_torrentWriterLock( tor );
791
792    tor->downloadedPrev += tor->downloadedCur;
793    tor->downloadedCur   = 0;
794    tor->uploadedPrev   += tor->uploadedCur;
795    tor->uploadedCur     = 0;
796
797    tr_torrentWriterUnlock( tor );
798}
799
800
801void
802tr_torrentSetHasPiece( tr_torrent_t * tor, int pieceIndex, int has )
803{
804    tr_torrentWriterLock( tor );
805
806    if( has )
807        tr_cpPieceAdd( tor->completion, pieceIndex );
808    else
809        tr_cpPieceRem( tor->completion, pieceIndex );
810
811    tr_torrentWriterUnlock( tor );
812}
813
814void tr_torrentRemoveSaved( tr_torrent_t * tor )
815{
816    tr_metainfoRemoveSaved( tor->info.hashString, tor->handle->tag );
817}
818
819void tr_torrentRecheck( tr_torrent_t * tor )
820{
821    tor->recheckFlag = TRUE;
822}
823
824
825int tr_torrentAttachPeer( tr_torrent_t * tor, tr_peer_t * peer )
826{
827    int i;
828    tr_peer_t * otherPeer;
829
830    assert( tor != NULL );
831    assert( peer != NULL );
832
833    if( tor->peerCount >= TR_MAX_PEER_COUNT )
834    {
835        tr_peerDestroy(  peer );
836        return 0;
837    }
838
839    /* Don't accept two connections from the same IP */
840    for( i = 0; i < tor->peerCount; i++ )
841    {
842        otherPeer = tor->peers[i];
843        if( !memcmp( tr_peerAddress( peer ), tr_peerAddress( otherPeer ), 4 ) )
844        {
845            tr_peerDestroy(  peer );
846            return 0;
847        }
848    }
849
850    tr_peerSetPrivate( peer, tor->info.flags & TR_FLAG_PRIVATE ||
851                       tor->pexDisabled );
852    tr_peerSetTorrent( peer, tor );
853    tor->peers[tor->peerCount++] = peer;
854
855    return 1;
856}
857
858int tr_torrentAddCompact( tr_torrent_t * tor, int from,
859                           uint8_t * buf, int count )
860{
861    struct in_addr addr;
862    in_port_t port;
863    int i, added;
864    tr_peer_t * peer;
865
866    added = 0;
867    for( i = 0; i < count; i++ )
868    {
869        memcpy( &addr, buf, 4 ); buf += 4;
870        memcpy( &port, buf, 2 ); buf += 2;
871
872        peer = tr_peerInit( addr, port, -1, from );
873        added += tr_torrentAttachPeer( tor, peer );
874    }
875
876    return added;
877}
878
879/***
880****
881***/
882
883static void setRunState( tr_torrent_t * tor, run_status_t run )
884{
885    tr_torrentWriterLock( tor );
886    tor->runStatus = run;
887    tr_torrentWriterUnlock( tor );
888}
889
890void tr_torrentStart( tr_torrent_t * tor )
891{
892    setRunState( tor, TR_RUN_RUNNING );
893}
894
895void tr_torrentStop( tr_torrent_t * tor )
896{
897    setRunState( tor, TR_RUN_STOPPING );
898}
899
900void tr_torrentClose( tr_torrent_t * tor )
901{
902    tr_torrentStop( tor );
903    tor->dieFlag = TRUE;
904}
905
906static void
907tr_torrentFree( tr_torrent_t * tor )
908{
909    tr_torrent_t * t;
910    tr_handle_t * h = tor->handle;
911    tr_info_t * inf = &tor->info;
912
913    tr_sharedLock( h->shared );
914
915    tr_rwClose( &tor->lock );
916    tr_cpClose( tor->completion );
917
918    tr_rcClose( tor->upload );
919    tr_rcClose( tor->download );
920    tr_rcClose( tor->swarmspeed );
921
922    tr_free( tor->destination );
923
924    tr_metainfoFree( inf );
925
926    if( tor == h->torrentList )
927        h->torrentList = tor->next;
928    else for( t=h->torrentList; t!=NULL; t=t->next ) {
929        if( t->next == tor ) {
930            t->next = tor->next;
931            break;
932        }
933    }
934
935    tr_free( tor );
936
937    h->torrentCount--;
938
939    tr_sharedUnlock( h->shared );
940}
941
942static void
943torrentThreadLoop ( void * _tor )
944{
945    static tr_lock_t checkFilesLock;
946    static int checkFilesLockInited = FALSE;
947    tr_torrent_t * tor = _tor;
948
949    /* create the check-files mutex */
950    if( !checkFilesLockInited ) {
951         checkFilesLockInited = TRUE;
952         tr_lockInit( &checkFilesLock );
953    }
954
955    /* loop until the torrent is being deleted */
956    while( ! ( tor->dieFlag && (tor->runStatus == TR_RUN_STOPPED) ) )
957    {
958        cp_status_t cpStatus;
959
960        /* sleep a little while */
961        tr_wait( tor->runStatus == TR_RUN_STOPPED ? 1600 : 800 );
962
963        /* if we're stopping... */
964        if( tor->runStatus == TR_RUN_STOPPING )
965        {
966            int i;
967            int peerCount;
968            uint8_t * peerCompact;
969            tr_torrentWriterLock( tor );
970
971            /* close the IO */
972            tr_ioClose( tor->io );
973            tor->io = NULL;
974
975            /* close the tracker */
976            tr_trackerStopped( tor->tracker );
977            tr_trackerPulse( tor->tracker, &peerCount, &peerCompact );
978            tr_trackerClose( tor->tracker );
979            tor->tracker = NULL;
980
981            /* close the peers */
982            for( i=0; i<tor->peerCount; ++i )
983                tr_peerDestroy( tor->peers[i] );
984            tor->peerCount = 0;
985
986            /* resest the transfer rates */
987            tr_rcReset( tor->download );
988            tr_rcReset( tor->upload );
989            tr_rcReset( tor->swarmspeed );
990
991            tor->stopDate = tr_date();
992            tor->runStatus = TR_RUN_STOPPED;
993
994            tr_torrentWriterUnlock( tor );
995        }
996
997        /* do we need to check files? */
998        if( tor->recheckFlag )
999        {
1000            if( !tr_lockTryLock( &checkFilesLock ) )
1001            {
1002                run_status_t realStatus;
1003
1004                tr_torrentWriterLock( tor );
1005                realStatus = tor->runStatus;
1006                tor->recheckFlag = FALSE;
1007                tor->runStatus = TR_RUN_CHECKING;
1008                tr_torrentWriterUnlock( tor );
1009
1010                tr_ioCheckFiles( tor, TR_RECHECK_FORCE );
1011                setRunState( tor, realStatus );
1012
1013                tr_lockUnlock( &checkFilesLock );
1014            }
1015            continue;
1016        }
1017
1018        /* if we're paused or stopped, not much to do... */
1019        if( tor->runStatus == TR_RUN_STOPPED )
1020            continue;
1021
1022        /* ping our peers if we're running... */
1023        if( tor->runStatus == TR_RUN_RUNNING )
1024        {
1025            int i;
1026            int peerCount;
1027            uint8_t * peerCompact;
1028
1029            /* starting to run... */
1030            if( tor->io == NULL ) {
1031                *tor->errorString = '\0';
1032                tr_torrentResetTransferStats( tor );
1033                tor->io = tr_ioInitFast( tor );
1034                if( tor->io == NULL ) {
1035                    tor->recheckFlag = TRUE;
1036                    continue;
1037                }
1038                tor->tracker = tr_trackerInit( tor );
1039                tor->startDate = tr_date();
1040            }
1041
1042            /* refresh our completion state */
1043            tr_torrentWriterLock( tor );
1044            cpStatus = tr_cpGetStatus( tor->completion );
1045            if( cpStatus != tor->cpStatus ) {
1046                tor->cpStatus = cpStatus;
1047                if( (cpStatus == TR_CP_COMPLETE)        /* if we're complete */
1048                    && tor->tracker!=NULL           /* and we have a tracker */
1049                    && tor->downloadedCur ) {        /* and it just happened */
1050                    tr_trackerCompleted( tor->tracker ); /* tell the tracker */
1051                    tor->hasChangedState = tor->cpStatus;  /* and the client */
1052                }
1053                tr_ioSync( tor->io );
1054            }
1055            tr_torrentWriterUnlock( tor );
1056
1057            /* ping the tracker... */
1058            tr_trackerPulse( tor->tracker, &peerCount, &peerCompact );
1059            if( peerCount > 0 ) {
1060                int used = tr_torrentAddCompact( tor, TR_PEER_FROM_TRACKER,
1061                                                 peerCompact, peerCount );
1062                tr_dbg( "got %i peers from announce, used %i", peerCount, used );
1063                free( peerCompact );
1064            }
1065
1066            /* Shuffle peers */
1067            if ( tor->peerCount > 1 ) {
1068                tr_peer_t * tmp = tor->peers[0];
1069                memmove( tor->peers, tor->peers+1,
1070                        (tor->peerCount-1) * sizeof(void*) );
1071                tor->peers[tor->peerCount - 1] = tmp;
1072            }
1073
1074            /* receive/send messages */
1075            tr_torrentWriterLock( tor );
1076            for( i=0; i<tor->peerCount; ) {
1077                tr_peer_t * peer = tor->peers[i];
1078                int ret = tr_peerPulse( peer );
1079                if( ret & TR_ERROR_IO_MASK ) {
1080                    tr_err( "Fatal error, stopping download (%d)", ret );
1081                    tor->runStatus = TR_RUN_STOPPING;
1082                    tor->error = ret;
1083                    strlcpy( tor->errorString,
1084                             tr_errorString(ret),
1085                             sizeof(tor->errorString) );
1086                    break;
1087                }
1088                if( ret ) {
1089                    tr_peerDestroy( peer );
1090                    tor->peerCount--;
1091                    memmove( &tor->peers[i], &tor->peers[i+1],
1092                             (tor->peerCount-i)*sizeof(void*) );
1093                    continue;
1094                }
1095                i++;
1096            }
1097            tr_torrentWriterUnlock( tor );
1098        }
1099    }
1100
1101    tr_ioClose( tor->io );
1102    tr_torrentFree( tor );
1103}
1104
1105
1106/***
1107****
1108****  File prioritization
1109****
1110***/
1111
1112static void
1113tr_torrentSetFilePriorityImpl( tr_torrent_t   * tor,
1114                               int              fileIndex,
1115                               tr_priority_t    priority,
1116                               int              doSave )
1117{
1118    int i;
1119    tr_file_t * file;
1120
1121    tr_torrentWriterLock( tor );
1122
1123    assert( tor != NULL );
1124    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
1125    assert( priority==TR_PRI_LOW || priority==TR_PRI_NORMAL || priority==TR_PRI_HIGH );
1126
1127    file = &tor->info.files[fileIndex];
1128    file->priority = priority;
1129    for( i=file->firstPiece; i<=file->lastPiece; ++i )
1130      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
1131
1132    tr_dbg ( "Setting file #%d (pieces %d-%d) priority to %d (%s)",
1133             fileIndex, file->firstPiece, file->lastPiece,
1134             priority, tor->info.files[fileIndex].name );
1135
1136    if( doSave )
1137        fastResumeSave( tor );
1138
1139    tr_torrentWriterUnlock( tor );
1140}
1141
1142void
1143tr_torrentSetFilePriority( tr_torrent_t   * tor,
1144                           int              fileIndex,
1145                           tr_priority_t    priority )
1146{
1147    tr_torrentSetFilePriorityImpl( tor, fileIndex, priority, TRUE );
1148}
1149
1150void
1151tr_torrentSetFilePriorities( tr_torrent_t         * tor,
1152                             const tr_priority_t  * filePriorities )
1153{
1154    int i;
1155    for( i=0; i<tor->info.pieceCount; ++i )
1156        tr_torrentSetFilePriorityImpl( tor, i, filePriorities[i], FALSE );
1157    fastResumeSave( tor );
1158}
1159
1160tr_priority_t
1161tr_torrentGetFilePriority( const tr_torrent_t *  tor, int file )
1162{
1163    tr_priority_t ret;
1164
1165    tr_torrentReaderLock( tor );
1166    assert( tor != NULL );
1167    assert( 0<=file && file<tor->info.fileCount );
1168    ret = tor->info.files[file].priority;
1169    tr_torrentReaderUnlock( tor );
1170
1171    return ret;
1172}
1173
1174
1175tr_priority_t*
1176tr_torrentGetFilePriorities( const tr_torrent_t * tor )
1177{
1178    int i;
1179    tr_priority_t * p;
1180
1181    tr_torrentReaderLock( tor );
1182    p = tr_new0( tr_priority_t, tor->info.fileCount );
1183    for( i=0; i<tor->info.fileCount; ++i )
1184        p[i] = tor->info.files[i].priority;
1185    tr_torrentReaderUnlock( tor );
1186
1187    return p;
1188}
1189
1190int
1191tr_torrentGetFileDL( const tr_torrent_t * tor,
1192                     int                  file )
1193{
1194    int do_download;
1195    tr_torrentReaderLock( tor );
1196
1197    assert( 0<=file && file<tor->info.fileCount );
1198    do_download = !tor->info.files[file].dnd;
1199
1200    tr_torrentReaderUnlock( tor );
1201    return do_download != 0;
1202}
1203
1204void
1205tr_torrentSetFileDL( tr_torrent_t  * tor,
1206                     int             file,
1207                     int             do_download )
1208{
1209    tr_torrentWriterLock( tor );
1210
1211    assert( 0<=file && file<tor->info.fileCount );
1212    tor->info.files[file].dnd = !do_download;
1213    fastResumeSave( tor );
1214
1215    tr_torrentWriterUnlock( tor );
1216}
Note: See TracBrowser for help on using the repository browser.