source: trunk/libtransmission/torrent.c @ 2374

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

re-randomize peer_id each time we start a torrent. (ticket #257)

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