source: trunk/libtransmission/torrent.c @ 2253

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

fix bug that caused libtransmission to think private torrents were public.

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