source: branches/encryption/libtransmission/torrent.c @ 3072

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

remove dead code: rwlock

  • Property svn:keywords set to Date Rev Author Id
File size: 33.4 KB
Line 
1/******************************************************************************
2 * $Id: torrent.c 3072 2007-09-15 17:09:00Z charles $
3 *
4 * Copyright (c) 2005-2007 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <assert.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29
30#include <sys/types.h>
31
32#include "transmission.h"
33#include "completion.h"
34#include "crypto.h" /* for tr_sha1 */
35#include "fastresume.h"
36#include "handshake.h"
37#include "inout.h"
38#include "metainfo.h"
39#include "net.h" /* tr_netNtop */
40#include "peer.h"
41#include "peer-mgr.h"
42#include "platform.h"
43#include "ratecontrol.h"
44#include "shared.h"
45#include "tracker.h"
46#include "trcompat.h" /* for strlcpy */
47#include "trevent.h"
48#include "utils.h"
49
50/***
51****
52***/
53
54tr_torrent*
55tr_torrentFindFromHash( tr_handle      * handle,
56                        const uint8_t  * torrentHash )
57{
58    tr_torrent * tor;
59
60    for( tor = handle->torrentList; tor; tor = tor->next )
61        if( !memcmp( tor->info.hash, torrentHash, SHA_DIGEST_LENGTH ) )
62            return tor;
63
64    return NULL;
65}
66
67tr_torrent*
68tr_torrentFindFromObfuscatedHash( tr_handle      * handle,
69                                  const uint8_t  * obfuscatedTorrentHash )
70{
71    tr_torrent * tor;
72
73    for( tor = handle->torrentList; tor; tor = tor->next )
74        if( !memcmp( tor->obfuscatedHash, obfuscatedTorrentHash, SHA_DIGEST_LENGTH ) )
75            return tor;
76
77    return NULL;
78}
79
80/***
81****  LOCKS
82***/
83
84void
85tr_torrentLock( const tr_torrent * tor )
86{
87    tr_lockLock ( (tr_lock*)tor->lock );
88}
89
90void
91tr_torrentUnlock( const tr_torrent * tor )
92{
93    tr_lockUnlock ( (tr_lock*)tor->lock );
94}
95
96/***
97****  PER-TORRENT UL / DL SPEEDS
98***/
99
100void
101tr_torrentSetSpeedMode( tr_torrent     * tor,
102                        int              up_or_down,
103                        tr_speedlimit_t  mode )
104{
105    tr_speedlimit_t * limit = up_or_down==TR_UP
106        ? &tor->uploadLimitMode
107        : &tor->downloadLimitMode;
108    *limit = mode;
109}
110
111tr_speedlimit_t
112tr_torrentGetSpeedMode( const tr_torrent * tor,
113                        int                up_or_down)
114{
115    return up_or_down==TR_UP ? tor->uploadLimitMode
116                             : tor->downloadLimitMode;
117}
118
119void
120tr_torrentSetSpeedLimit( tr_torrent   * tor,
121                         int            up_or_down,
122                         int            single_KiB_sec )
123{
124    tr_ratecontrol * rc = up_or_down==TR_UP ? tor->upload : tor->download;
125    tr_rcSetLimit( rc, single_KiB_sec );
126}
127
128int
129tr_torrentGetSpeedLimit( const tr_torrent  * tor,
130                         int                 up_or_down )
131{
132    tr_ratecontrol * rc = up_or_down==TR_UP ? tor->upload : tor->download;
133    return tr_rcGetLimit( rc );
134}
135
136/***
137****
138***/
139
140static void
141onTrackerResponse( void * tracker UNUSED, void * vevent, void * user_data )
142{
143    tr_torrent * tor = (tr_torrent *) user_data;
144    tr_tracker_event_t * event = (tr_tracker_event_t *) vevent;
145
146    switch( event->messageType )
147    {
148        case TR_TRACKER_PEERS:
149            tr_peerMgrAddPeers( tor->handle->peerMgr,
150                                tor->info.hash,
151                                TR_PEER_FROM_TRACKER,
152                                event->peerCompact,
153                                event->peerCount );
154            break;
155
156        case TR_TRACKER_WARNING:
157            tr_err( "Tracker: Warning - %s", event->text );
158            tor->error = TR_ERROR_TC_WARNING;
159            strlcpy( tor->errorString, event->text, sizeof(tor->errorString) );
160            break;
161
162        case TR_TRACKER_ERROR:
163            tr_err( "Tracker: Error - %s", event->text );
164            tor->error = TR_ERROR_TC_ERROR;
165            strlcpy( tor->errorString, event->text, sizeof(tor->errorString) );
166            break;
167
168        case TR_TRACKER_STOPPED:
169            assert( tor->runStatus == TR_RUN_STOPPING );
170            tor->runStatus = TR_RUN_STOPPED;
171            break;
172    }
173}
174
175/***
176****
177****  TORRENT INSTANTIATION
178****
179***/
180
181static int
182getBytePiece( const tr_info_t * info, uint64_t byteOffset )
183{
184    assert( info != NULL );
185    assert( info->pieceSize != 0 );
186
187    return byteOffset / info->pieceSize;
188}
189
190static void
191initFilePieces ( tr_info_t * info, int fileIndex )
192{
193    tr_file_t * file = &info->files[fileIndex];
194    uint64_t firstByte, lastByte;
195
196    assert( info != NULL );
197    assert( 0<=fileIndex && fileIndex<info->fileCount );
198
199    file = &info->files[fileIndex];
200    firstByte = file->offset;
201    lastByte = firstByte + (file->length ? file->length-1 : 0);
202    file->firstPiece = getBytePiece( info, firstByte );
203    file->lastPiece = getBytePiece( info, lastByte );
204}
205
206static tr_priority_t
207calculatePiecePriority ( const tr_torrent * tor,
208                         int                piece )
209{
210    int i;
211    tr_priority_t priority = TR_PRI_NORMAL;
212
213    for( i=0; i<tor->info.fileCount; ++i )
214    {
215        const tr_file_t * file = &tor->info.files[i];
216        if ( file->firstPiece <= piece
217          && file->lastPiece  >= piece
218          && file->priority   >  priority)
219              priority = file->priority;
220
221        /* when dealing with multimedia files, getting the first and
222           last pieces can sometimes allow you to preview it a bit
223           before it's fully downloaded... */
224        if ( file->priority >= TR_PRI_NORMAL )
225            if ( file->firstPiece == piece || file->lastPiece == piece )
226                priority = TR_PRI_HIGH;
227    }
228
229    return priority;
230}
231
232static void
233tr_torrentInitFilePieces( tr_torrent * tor )
234{
235    int i;
236    uint64_t offset = 0;
237
238    assert( tor != NULL );
239
240    for( i=0; i<tor->info.fileCount; ++i ) {
241      tor->info.files[i].offset = offset;
242      offset += tor->info.files[i].length;
243      initFilePieces( &tor->info, i );
244    }
245
246    for( i=0; i<tor->info.pieceCount; ++i )
247        tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
248}
249
250static void recheckCpState( tr_torrent * );
251
252static void
253tr_torrentStartImpl( tr_torrent * tor )
254{
255    assert( tor != NULL );
256    assert( tor->runStatus == TR_RUN_RUNNING );
257
258    *tor->errorString = '\0';
259    tr_torrentResetTransferStats( tor );
260    recheckCpState( tor );
261    tor->startDate = tr_date();
262    tr_trackerStart( tor->tracker );
263    tr_peerMgrStartTorrent( tor->handle->peerMgr, tor->info.hash );
264}
265
266static void
267recheckDoneCB( tr_torrent * tor )
268{
269    tr_peerMgrUpdateCompletion( tor->handle->peerMgr,
270                                tor->info.hash );
271
272    recheckCpState( tor );
273
274    if( tor->runStatus == TR_RUN_RUNNING )
275        tr_torrentStartImpl( tor );
276}
277
278static void
279torrentRealInit( tr_handle_t * h,
280                 tr_torrent  * tor,
281                 const char  * destination,
282                 int           flags )
283{
284    uint64_t loaded;
285    uint64_t t;
286    tr_bitfield * uncheckedPieces;
287    tr_info_t * info = &tor->info;
288   
289    tor->info.flags |= flags;
290
291    tr_sharedLock( h->shared );
292
293    tor->destination = tr_strdup( destination );
294
295    tor->handle   = h;
296    tor->azId     = h->azId;
297    tor->hasChangedState = -1;
298    tor->pexDisabled = 0;
299
300    tor->runStatusToSaveIsSet = FALSE;
301
302    /**
303     * Decide on a block size.  constraints:
304     * (1) most clients decline requests over 16 KiB
305     * (2) pieceSize must be a multiple of block size
306     */
307    tor->blockSize = info->pieceSize;
308    while( tor->blockSize > (1024*16) )
309        tor->blockSize /= 2;
310
311    tor->lastPieceSize = info->totalSize % info->pieceSize;
312
313    if( !tor->lastPieceSize )
314         tor->lastPieceSize = info->pieceSize;
315
316    tor->lastBlockSize = info->totalSize % tor->blockSize;
317
318    if( !tor->lastBlockSize )
319         tor->lastBlockSize = tor->blockSize;
320
321    tor->blockCount =
322        ( info->totalSize + tor->blockSize - 1 ) / tor->blockSize;
323
324    tor->blockCountInPiece =
325        info->pieceSize / tor->blockSize;
326
327    tor->blockCountInLastPiece =
328        ( tor->lastPieceSize + tor->blockSize - 1 ) / tor->blockSize;
329
330    /* check our work */
331    assert( ( info->pieceSize % tor->blockSize ) == 0 );
332    t = info->pieceCount - 1;
333    t *= info->pieceSize;
334    t += tor->lastPieceSize;
335    assert( t == info->totalSize );
336    t = tor->blockCount - 1;
337    t *= tor->blockSize;
338    t += tor->lastBlockSize;
339    assert( t == info->totalSize );
340    t = info->pieceCount - 1;
341    t *= tor->blockCountInPiece;
342    t += tor->blockCountInLastPiece;
343    assert( t == (uint64_t)tor->blockCount );
344
345    tor->completion = tr_cpInit( tor );
346
347    tr_torrentInitFilePieces( tor );
348
349    tor->lock = tr_lockNew( );
350
351    tor->upload         = tr_rcInit();
352    tor->download       = tr_rcInit();
353    tor->swarmspeed     = tr_rcInit();
354
355    tr_sha1( tor->obfuscatedHash, "req2", 4,
356                                  info->hash, SHA_DIGEST_LENGTH,
357                                  NULL );
358 
359    /* We have a new torrent */
360    tor->publicPort = tr_sharedGetPublicPort( h->shared );
361
362    tr_sharedUnlock( h->shared );
363
364    tr_peerMgrAddTorrent( h->peerMgr, tor );
365
366    if( !h->isPortSet )
367        tr_setBindPort( h, TR_DEFAULT_PORT );
368
369    assert( !tor->downloadedCur );
370    assert( !tor->uploadedCur );
371
372    tor->error   = TR_OK;
373
374    uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
375    loaded = tr_fastResumeLoad( tor, uncheckedPieces );
376
377    /* the `paused' flag has highest precedence...
378       after that, the fastresume setting is used...
379       if that's not found, default to RUNNING */
380    if( flags & TR_FLAG_PAUSED )
381        tor->runStatus = TR_RUN_STOPPED;
382    else if( !(loaded & TR_FR_RUN ) )
383        tor->runStatus = TR_RUN_RUNNING;
384
385    if( tr_bitfieldIsEmpty( uncheckedPieces ) )
386        tr_bitfieldFree( uncheckedPieces );
387    else
388        tor->uncheckedPieces = uncheckedPieces;
389
390
391    if( !(loaded & TR_FR_SPEEDLIMIT ) ) {
392        int limit, enabled;
393        tr_getGlobalSpeedLimit( tor->handle, TR_UP, &enabled, &limit );
394        tr_torrentSetSpeedLimit( tor, TR_UP, limit );
395        tr_getGlobalSpeedLimit( tor->handle, TR_DOWN, &enabled, &limit );
396        tr_torrentSetSpeedLimit( tor, TR_DOWN, limit );
397    }
398
399    tor->cpStatus = tr_cpGetStatus( tor->completion );
400
401    tor->tracker = tr_trackerNew( tor );
402    tor->trackerSubscription = tr_trackerSubscribe( tor->tracker, onTrackerResponse, tor );
403
404    tr_sharedLock( h->shared );
405    tor->next = h->torrentList;
406    h->torrentList = tor;
407    h->torrentCount++;
408    tr_sharedUnlock( h->shared );
409
410    tr_ioRecheckAdd( tor, recheckDoneCB, tor->runStatus );
411}
412
413static int
414pathIsInUse ( const tr_handle_t   * h,
415              const char          * destination,
416              const char          * name )
417{
418    const tr_torrent * tor;
419   
420    for( tor=h->torrentList; tor; tor=tor->next )
421        if( !strcmp( destination, tor->destination )
422         && !strcmp( name, tor->info.name ) )
423            return TRUE;
424
425    return FALSE;
426}
427
428static int
429hashExists( const tr_handle_t   * h,
430            const uint8_t       * hash )
431{
432    const tr_torrent * tor;
433
434    for( tor=h->torrentList; tor; tor=tor->next )
435        if( !memcmp( hash, tor->info.hash, SHA_DIGEST_LENGTH ) )
436            return TRUE;
437
438    return FALSE;
439}
440
441static int
442infoCanAdd( const tr_handle_t   * h,
443            const char          * destination,
444            const tr_info_t     * info )
445{
446    if( hashExists( h, info->hash ) )
447        return TR_EDUPLICATE;
448
449    if( destination && pathIsInUse( h, destination, info->name ) )
450        return TR_EDUPLICATE;
451
452    return TR_OK;
453}
454
455int
456tr_torrentParse( const tr_handle_t  * h,
457                 const char         * path,
458                 const char         * destination,
459                 tr_info_t          * setme_info )
460{
461    int ret, doFree;
462    tr_info_t tmp;
463
464    if( setme_info == NULL )
465        setme_info = &tmp;
466
467    memset( setme_info, 0, sizeof( tr_info_t ) );
468    ret = tr_metainfoParseFile( setme_info, h->tag, path, FALSE );
469    doFree = !ret && (setme_info == &tmp);
470
471    if( ret == TR_OK )
472        ret = infoCanAdd( h, destination, setme_info );
473
474    if( doFree )
475        tr_metainfoFree( &tmp );
476
477    return ret;
478}
479 
480tr_torrent *
481tr_torrentInit( tr_handle_t   * h,
482                const char    * path,
483                const char    * destination,
484                int             flags,
485                int           * error )
486{
487    int val;
488    int tmpError;
489    tr_torrent * tor = NULL;
490
491    if( !error )
492         error = &tmpError;
493
494    if(( val = tr_torrentParse( h, path, destination, NULL )))
495        *error = val;
496    else if(!(( tor = tr_new0( tr_torrent, 1 ))))
497        *error = TR_EOTHER;
498    else {
499        tr_metainfoParseFile( &tor->info, h->tag, path, TR_FLAG_SAVE & flags );
500        torrentRealInit( h, tor, destination, flags );
501    }
502
503    return tor;
504}
505
506int
507tr_torrentParseHash( const tr_handle_t  * h,
508                     const char         * hashStr,
509                     const char         * destination,
510                     tr_info_t          * setme_info )
511{
512    int ret, doFree;
513    tr_info_t tmp;
514
515    if( setme_info == NULL )
516        setme_info = &tmp;
517
518    memset( setme_info, 0, sizeof( tr_info_t ) );
519    ret = tr_metainfoParseHash( setme_info, h->tag, hashStr );
520    doFree = !ret && (setme_info == &tmp);
521
522    if( ret == TR_OK )
523        ret = infoCanAdd( h, destination, setme_info );
524
525    if( doFree )
526        tr_metainfoFree( &tmp );
527
528    return ret;
529}
530
531tr_torrent *
532tr_torrentInitSaved( tr_handle_t    * h,
533                     const char     * hashStr,
534                     const char     * destination,
535                     int              flags,
536                     int            * error )
537{
538    int val;
539    int tmpError;
540    tr_torrent * tor = NULL;
541
542    if( !error )
543         error = &tmpError;
544
545    if(( val = tr_torrentParseHash( h, hashStr, destination, NULL )))
546        *error = val;
547    else if(!(( tor = tr_new0( tr_torrent, 1 ))))
548        *error = TR_EOTHER;
549    else {
550        tr_metainfoParseHash( &tor->info, h->tag, hashStr );
551        torrentRealInit( h, tor, destination, (TR_FLAG_SAVE|flags) );
552    }
553
554    return tor;
555}
556
557static int
558tr_torrentParseData( const tr_handle_t  * h,
559                     const uint8_t      * data,
560                     size_t               size,
561                     const char         * destination,
562                     tr_info_t          * setme_info )
563{
564    int ret, doFree;
565    tr_info_t tmp;
566
567    if( setme_info == NULL )
568        setme_info = &tmp;
569
570    memset( setme_info, 0, sizeof( tr_info_t ) );
571    ret = tr_metainfoParseData( setme_info, h->tag, data, size, FALSE );
572    doFree = !ret && (setme_info == &tmp);
573
574    if( ret == TR_OK )
575        ret = infoCanAdd( h, destination, setme_info );
576
577    if( doFree )
578        tr_metainfoFree( &tmp );
579
580    return ret;
581}
582
583tr_torrent *
584tr_torrentInitData( tr_handle_t    * h,
585                    const uint8_t  * data,
586                    size_t           size,
587                    const char     * destination,
588                    int              flags,
589                    int            * error )
590{
591    int val;
592    int tmpError;
593    tr_torrent * tor = NULL;
594
595    if( !error )
596         error = &tmpError;
597
598    if(( val = tr_torrentParseData( h, data, size, destination, NULL )))
599        *error = val;
600    else if(!(( tor = tr_new0( tr_torrent, 1 ))))
601        *error = TR_EOTHER;
602    else {
603        tr_metainfoParseData( &tor->info, h->tag, data, size, TR_FLAG_SAVE & flags );
604        torrentRealInit( h, tor, destination, flags );
605    }
606
607    return tor;
608}
609
610const tr_info_t *
611tr_torrentInfo( const tr_torrent * tor )
612{
613    return &tor->info;
614}
615
616/***
617****
618***/
619
620#if 0
621int tr_torrentScrape( tr_torrent * tor, int * s, int * l, int * d )
622{
623    return tr_trackerScrape( tor, s, l, d );
624}
625#endif
626
627static int
628saveFastResumeNow( void * vtor )
629{
630    tr_torrent * tor = (tr_torrent *) vtor;
631
632    tr_fastResumeSave( tor );
633    recheckCpState( tor );
634
635    tr_timerFree( &tor->saveTimer );
636    return FALSE;
637}
638
639static void
640saveFastResumeSoon( void * vtor )
641{
642    tr_torrent * tor = (tr_torrent *) vtor;
643
644    if( tor->saveTimer == NULL )
645        tor->saveTimer = tr_timerNew( tor->handle, saveFastResumeNow, tor, 1000 );
646}
647
648/**
649***
650**/
651
652void
653tr_torrentSetFolder( tr_torrent * tor, const char * path )
654{
655    tr_free( tor->destination );
656    tor->destination = tr_strdup( path );
657    saveFastResumeSoon( tor );
658}
659
660const char*
661tr_torrentGetFolder( const tr_torrent * tor )
662{
663    return tor->destination;
664}
665
666void
667tr_torrentChangeMyPort( tr_torrent * tor, int port )
668{
669    tr_torrentLock( tor );
670
671    tor->publicPort = port;
672
673    if( tor->tracker )
674        tr_trackerChangeMyPort( tor->tracker );
675
676    tr_torrentUnlock( tor );
677}
678
679/***********************************************************************
680 * torrentReallyStop
681 ***********************************************************************
682 * Joins the download thread and frees/closes everything related to it.
683 **********************************************************************/
684
685void tr_torrentDisablePex( tr_torrent * tor, int disable )
686{
687    tr_torrentLock( tor );
688
689    if( ! ( TR_FLAG_PRIVATE & tor->info.flags ) )
690    {
691        if( tor->pexDisabled != disable )
692        {
693            tor->pexDisabled = disable;
694            tr_peerMgrDisablePex( tor->handle->peerMgr,
695                                  tor->info.hash,
696                                  tor->pexDisabled );
697        }
698    }
699
700    tr_torrentUnlock( tor );
701}
702
703static int tr_didStateChangeTo ( tr_torrent * tor, int status )
704{
705    int ret;
706
707    tr_torrentLock( tor );
708    if (( ret = tor->hasChangedState == status ))
709        tor->hasChangedState = -1;
710    tr_torrentUnlock( tor );
711
712    return ret;
713}
714
715int tr_getIncomplete( tr_torrent * tor )
716{
717    return tr_didStateChangeTo( tor, TR_CP_INCOMPLETE );
718}
719int tr_getDone( tr_torrent * tor )
720{
721    return tr_didStateChangeTo( tor, TR_CP_DONE );
722}
723int tr_getComplete( tr_torrent * tor )
724{
725    return tr_didStateChangeTo( tor, TR_CP_COMPLETE );
726}
727
728void
729tr_manualUpdate( tr_torrent * tor )
730{
731    if( tor->runStatus == TR_RUN_RUNNING )
732    tr_trackerReannounce( tor->tracker );
733}
734int
735tr_torrentCanManualUpdate( const tr_torrent * tor )
736{
737    return ( tor != NULL )
738        && ( tor->runStatus == TR_RUN_RUNNING )
739        && ( tr_trackerCanManualAnnounce( tor->tracker ) );
740}
741
742const tr_stat_t *
743tr_torrentStat( tr_torrent * tor )
744{
745    tr_stat_t * s;
746    struct tr_tracker * tc;
747
748    tr_torrentLock( tor );
749
750    tor->statCur = ( tor->statCur + 1 ) % 2;
751    s = &tor->stats[tor->statCur];
752
753    s->error  = tor->error;
754    memcpy( s->errorString, tor->errorString,
755            sizeof( s->errorString ) );
756
757    tc = tor->tracker;
758    s->tracker = tr_trackerGetAddress( tor->tracker );
759
760    tr_peerMgrTorrentStats( tor->handle->peerMgr,
761                            tor->info.hash,
762                            &s->peersTotal,
763                            &s->peersConnected,
764                            &s->peersSendingToUs,
765                            &s->peersGettingFromUs,
766                             s->peersFrom );
767
768    s->percentComplete = tr_cpPercentComplete ( tor->completion );
769
770    s->percentDone = tr_cpPercentDone( tor->completion );
771    s->left = tr_cpLeftUntilDone( tor->completion );
772
773    switch( tor->runStatus ) {
774        case TR_RUN_CHECKING_WAIT: s->status = TR_STATUS_CHECK_WAIT; break;
775        case TR_RUN_CHECKING: s->status = TR_STATUS_CHECK; break;
776        case TR_RUN_STOPPING: s->status = TR_STATUS_STOPPING; break;
777        case TR_RUN_STOPPED: s->status = TR_STATUS_STOPPED; break;
778        case TR_RUN_RUNNING: switch( tor->cpStatus ) {
779            case TR_CP_INCOMPLETE: s->status = TR_STATUS_DOWNLOAD; break;
780            case TR_CP_DONE: s->status = TR_STATUS_DONE; break;
781            case TR_CP_COMPLETE: s->status = TR_STATUS_SEED; break;
782        }
783    }
784
785    s->recheckProgress = (tor->uncheckedPieces == NULL)
786        ? 0.0
787        : 1.0 - ((double)tr_bitfieldCountTrueBits(tor->uncheckedPieces) / tor->info.pieceCount);
788
789    s->cpStatus = tor->cpStatus;
790
791    /* tr_rcRate() doesn't make the difference between 'piece'
792       messages and other messages, which causes a non-zero
793       download rate even tough we are not downloading. So we
794       force it to zero not to confuse the user. */
795    s->rateDownload = tor->runStatus==TR_RUN_RUNNING
796        ? tr_rcRate( tor->download )
797        : 0.0;
798    s->rateUpload = tr_rcRate( tor->upload );
799   
800    tr_trackerGetCounts( tc,
801                         &s->completedFromTracker,
802                         &s->leechers, 
803                         &s->seeders );
804
805    s->swarmspeed = tr_rcRate( tor->swarmspeed );
806   
807    s->startDate = tor->startDate;
808    s->activityDate = tor->activityDate;
809
810    s->eta = s->rateDownload < 0.1
811        ? -1.0f
812        : (s->left / s->rateDownload / 1024.0);
813
814    s->corrupt         = tor->corruptCur    + tor->corruptPrev;
815    s->uploaded        = tor->uploadedCur   + tor->uploadedPrev;
816    s->downloaded      = tor->downloadedCur + tor->downloadedPrev;
817    s->downloadedValid = tr_cpDownloadedValid( tor->completion );
818   
819    s->ratio = s->downloaded || s->downloadedValid
820      ? (float)s->uploaded / (float)MAX(s->downloaded, s->downloadedValid)
821      : TR_RATIO_NA; 
822   
823    tr_torrentUnlock( tor );
824
825    return s;
826}
827
828/***
829****
830***/
831
832static uint64_t
833fileBytesCompleted ( const tr_torrent * tor, int fileIndex )
834{
835    const tr_file_t * file     =  &tor->info.files[fileIndex];
836    const uint64_t firstBlock       =  file->offset / tor->blockSize;
837    const uint64_t firstBlockOffset =  file->offset % tor->blockSize;
838    const uint64_t lastOffset       =  file->length ? (file->length-1) : 0;
839    const uint64_t lastBlock        = (file->offset + lastOffset) / tor->blockSize;
840    const uint64_t lastBlockOffset  = (file->offset + lastOffset) % tor->blockSize;
841    uint64_t haveBytes = 0;
842
843    assert( tor != NULL );
844    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
845    assert( file->offset + file->length <= tor->info.totalSize );
846    assert( ( (int)firstBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
847    assert( ( (int)lastBlock < tor->blockCount ) || (!file->length && file->offset==tor->info.totalSize) );
848    assert( firstBlock <= lastBlock );
849    assert( (int)tr_torBlockPiece( tor, firstBlock ) == file->firstPiece );
850    assert( (int)tr_torBlockPiece( tor, lastBlock ) == file->lastPiece );
851
852    if( firstBlock == lastBlock )
853    {
854        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
855            haveBytes += lastBlockOffset + 1 - firstBlockOffset;
856    }
857    else
858    {
859        uint64_t i;
860
861        if( tr_cpBlockIsComplete( tor->completion, firstBlock ) )
862            haveBytes += tor->blockSize - firstBlockOffset;
863
864        for( i=firstBlock+1; i<lastBlock; ++i )
865            if( tr_cpBlockIsComplete( tor->completion, i ) )
866               haveBytes += tor->blockSize;
867
868        if( tr_cpBlockIsComplete( tor->completion, lastBlock ) )
869            haveBytes += lastBlockOffset + 1;
870    }
871
872    return haveBytes;
873}
874
875tr_file_stat_t *
876tr_torrentFiles( const tr_torrent * tor, int * fileCount )
877{
878    int i;
879    const int n = tor->info.fileCount;
880    tr_file_stat_t * files = tr_new0( tr_file_stat_t, n );
881    tr_file_stat_t * walk = files;
882
883    for( i=0; i<n; ++i, ++walk )
884    {
885        const tr_file_t * file = tor->info.files + i;
886        cp_status_t cp;
887
888        walk->bytesCompleted = fileBytesCompleted( tor, i );
889
890        walk->progress = file->length
891            ? walk->bytesCompleted / (float)file->length
892            : 1.0;
893
894        if( walk->bytesCompleted >= file->length )
895            cp = TR_CP_COMPLETE;
896        else if( tor->info.files[i].dnd )
897            cp = TR_CP_DONE;
898        else
899            cp = TR_CP_INCOMPLETE;
900
901        walk->completionStatus = cp;
902    }
903
904    *fileCount = n;
905
906    return files;
907}
908
909void
910tr_torrentFilesFree( tr_file_stat_t * files, int fileCount UNUSED )
911{
912    tr_free( files );
913}
914
915/***
916****
917***/
918
919tr_peer_stat *
920tr_torrentPeers( const tr_torrent * tor, int * peerCount )
921{
922    return tr_peerMgrPeerStats( tor->handle->peerMgr,
923                                tor->info.hash, peerCount );
924}
925
926void tr_torrentPeersFree( tr_peer_stat * peers, int peerCount UNUSED )
927{
928    tr_free( peers );
929}
930
931void tr_torrentAvailability( const tr_torrent * tor, int8_t * tab, int size )
932{
933    return tr_peerMgrTorrentAvailability( tor->handle->peerMgr,
934                                          tor->info.hash,
935                                          tab, size );
936}
937
938void tr_torrentAmountFinished( const tr_torrent * tor, float * tab, int size )
939{
940    int i;
941    float interval;
942    tr_torrentLock( tor );
943
944    interval = (float)tor->info.pieceCount / (float)size;
945    for( i = 0; i < size; i++ )
946    {
947        int piece = i * interval;
948        tab[i] = tr_cpPercentBlocksInPiece( tor->completion, piece );
949    }
950
951    tr_torrentUnlock( tor );
952}
953
954void
955tr_torrentResetTransferStats( tr_torrent * tor )
956{
957    tr_torrentLock( tor );
958
959    tor->downloadedPrev += tor->downloadedCur;
960    tor->downloadedCur   = 0;
961    tor->uploadedPrev   += tor->uploadedCur;
962    tor->uploadedCur     = 0;
963    tor->corruptPrev    += tor->corruptCur;
964    tor->corruptCur      = 0;
965
966    tr_torrentUnlock( tor );
967}
968
969
970void
971tr_torrentSetHasPiece( tr_torrent * tor, int pieceIndex, int has )
972{
973    tr_torrentLock( tor );
974
975    if( has )
976        tr_cpPieceAdd( tor->completion, pieceIndex );
977    else
978        tr_cpPieceRem( tor->completion, pieceIndex );
979
980    tr_torrentUnlock( tor );
981}
982
983void tr_torrentRemoveSaved( tr_torrent * tor )
984{
985    tr_metainfoRemoveSaved( tor->info.hashString, tor->handle->tag );
986}
987
988void tr_torrentRecheck( tr_torrent * tor )
989{
990    if( !tor->uncheckedPieces )
991        tor->uncheckedPieces = tr_bitfieldNew( tor->info.pieceCount );
992    tr_bitfieldAddRange( tor->uncheckedPieces, 0, tor->info.pieceCount );
993
994    tr_ioRecheckAdd( tor, recheckDoneCB, tor->runStatus );
995}
996
997/***
998****
999***/
1000
1001void
1002tr_torrentStart( tr_torrent * tor )
1003{
1004    tr_ioRecheckAdd( tor, recheckDoneCB, TR_RUN_RUNNING );
1005}
1006
1007static void
1008tr_torrentFree( tr_torrent * tor )
1009{
1010    tr_torrent * t;
1011    tr_handle_t * h = tor->handle;
1012    tr_info_t * inf = &tor->info;
1013fprintf( stderr, "closing torrent %s\n", tor->info.name );
1014
1015    assert( tor != NULL );
1016    assert( tor->runStatus == TR_RUN_STOPPED );
1017
1018    tr_sharedLock( h->shared );
1019
1020    tr_lockFree( tor->lock );
1021    tr_cpClose( tor->completion );
1022
1023    tr_rcClose( tor->upload );
1024    tr_rcClose( tor->download );
1025    tr_rcClose( tor->swarmspeed );
1026
1027    tr_trackerUnsubscribe( tor->tracker, tor->trackerSubscription );
1028    tr_trackerFree( tor->tracker );
1029    tor->tracker = NULL;
1030
1031    tr_free( tor->destination );
1032
1033    if( tor == h->torrentList )
1034        h->torrentList = tor->next;
1035    else for( t=h->torrentList; t!=NULL; t=t->next ) {
1036        if( t->next == tor ) {
1037            t->next = tor->next;
1038            break;
1039        }
1040    }
1041
1042    assert( h->torrentCount >= 1 );
1043    h->torrentCount--;
1044
1045    tr_inf( "closing torrent %s... %d torrents left",
1046            tor->info.name, h->torrentCount );
1047
1048    tr_peerMgrRemoveTorrent( h->peerMgr, tor->info.hash );
1049
1050    tr_metainfoFree( inf );
1051    tr_free( tor );
1052
1053    tr_sharedUnlock( h->shared );
1054}
1055
1056static int
1057freeWhenStopped( void * vtor )
1058{
1059    tr_torrent * tor = vtor;
1060
1061    if( tor->runStatus != TR_RUN_STOPPED ) /* keep waiting */
1062        return TRUE;
1063
1064    tr_torrentFree( tor );
1065    return FALSE;
1066}
1067
1068static void
1069tr_torrentStopImpl( void * vtor )
1070{
1071    tr_torrent * tor = vtor;
1072
1073    switch( tor->runStatus )
1074    {
1075        case TR_RUN_CHECKING_WAIT:
1076        case TR_RUN_CHECKING:
1077            tr_ioRecheckRemove( tor );
1078            tr_torrentStop( tor );
1079            break;
1080
1081        case TR_RUN_RUNNING:
1082            saveFastResumeNow( tor );
1083            tr_peerMgrStopTorrent( tor->handle->peerMgr, tor->info.hash );
1084            tor->runStatus = TR_RUN_STOPPING;
1085            tor->stopDate = tr_date( );
1086            tr_trackerStop( tor->tracker );
1087            tr_ioClose( tor );
1088            break;
1089
1090        case TR_RUN_STOPPING:
1091        case TR_RUN_STOPPED:
1092            break;
1093    }
1094}
1095
1096void
1097tr_torrentStop( tr_torrent * tor )
1098{
1099    tr_runInEventThread( tor->handle, tr_torrentStopImpl, tor );
1100}
1101
1102void
1103tr_torrentClose( tr_torrent * tor )
1104{
1105    tor->runStatusToSave = tor->runStatus;
1106    tor->runStatusToSaveIsSet = TRUE;
1107    tor->dieFlag = TRUE;
1108    tr_torrentStop( tor );
1109    tr_timerNew( tor->handle, freeWhenStopped, tor, 250 );
1110}
1111
1112static void
1113recheckCpState( tr_torrent * tor )
1114{
1115    cp_status_t cpStatus;
1116
1117    tr_torrentLock( tor );
1118
1119    cpStatus = tr_cpGetStatus( tor->completion );
1120    if( cpStatus != tor->cpStatus ) {
1121        tor->cpStatus = cpStatus;
1122        tor->hasChangedState = tor->cpStatus;  /* tell the client... */
1123        if( (cpStatus == TR_CP_COMPLETE) /* ...and if we're complete */
1124            && tor->downloadedCur ) {        /* and it just happened */
1125            tr_trackerCompleted( tor->tracker ); /* tell the tracker */
1126        }
1127        tr_ioClose( tor );
1128        saveFastResumeSoon( tor );
1129    }
1130    tr_torrentUnlock( tor );
1131}
1132
1133/**
1134***  File priorities
1135**/
1136
1137void
1138tr_torrentSetFilePriority( tr_torrent   * tor,
1139                           int            fileIndex,
1140                           tr_priority_t  priority )
1141{
1142    int i;
1143    tr_file_t * file;
1144
1145    tr_torrentLock( tor );
1146
1147    assert( tor != NULL );
1148    assert( 0<=fileIndex && fileIndex<tor->info.fileCount );
1149    assert( priority==TR_PRI_LOW || priority==TR_PRI_NORMAL || priority==TR_PRI_HIGH );
1150
1151    file = &tor->info.files[fileIndex];
1152    file->priority = priority;
1153    for( i=file->firstPiece; i<=file->lastPiece; ++i )
1154      tor->info.pieces[i].priority = calculatePiecePriority( tor, i );
1155
1156    tr_dbg ( "Setting file #%d (pieces %d-%d) priority to %d (%s)",
1157             fileIndex, file->firstPiece, file->lastPiece,
1158             priority, tor->info.files[fileIndex].name );
1159
1160    saveFastResumeSoon( tor );
1161
1162    tr_torrentUnlock( tor );
1163}
1164
1165void
1166tr_torrentSetFilePriorities( tr_torrent     * tor,
1167                             int            * files,
1168                             int              fileCount,
1169                             tr_priority_t    priority )
1170{
1171    int i;
1172    for( i=0; i<fileCount; ++i )
1173        tr_torrentSetFilePriority( tor, files[i], priority );
1174}
1175
1176tr_priority_t
1177tr_torrentGetFilePriority( const tr_torrent *  tor, int file )
1178{
1179    tr_priority_t ret;
1180
1181    tr_torrentLock( tor );
1182    assert( tor != NULL );
1183    assert( 0<=file && file<tor->info.fileCount );
1184    ret = tor->info.files[file].priority;
1185    tr_torrentUnlock( tor );
1186
1187    return ret;
1188}
1189
1190
1191tr_priority_t*
1192tr_torrentGetFilePriorities( const tr_torrent * tor )
1193{
1194    int i;
1195    tr_priority_t * p;
1196
1197    tr_torrentLock( tor );
1198    p = tr_new0( tr_priority_t, tor->info.fileCount );
1199    for( i=0; i<tor->info.fileCount; ++i )
1200        p[i] = tor->info.files[i].priority;
1201    tr_torrentUnlock( tor );
1202
1203    return p;
1204}
1205
1206/**
1207***  File DND
1208**/
1209
1210int
1211tr_torrentGetFileDL( const tr_torrent * tor,
1212                     int                file )
1213{
1214    int doDownload;
1215    tr_torrentLock( tor );
1216
1217    assert( 0<=file && file<tor->info.fileCount );
1218    doDownload = !tor->info.files[file].dnd;
1219
1220    tr_torrentUnlock( tor );
1221    return doDownload != 0;
1222}
1223
1224void
1225tr_torrentSetFileDL( tr_torrent  * tor,
1226                     int           fileIndex,
1227                     int           doDownload )
1228{
1229    tr_file_t * file;
1230    const int dnd = !doDownload;
1231    int firstPiece, firstPieceDND;
1232    int lastPiece, lastPieceDND;
1233    int i;
1234
1235    tr_torrentLock( tor );
1236
1237    file = &tor->info.files[fileIndex];
1238    file->dnd = dnd;
1239    firstPiece = file->firstPiece;
1240    lastPiece = file->lastPiece;
1241
1242    /* can't set the first piece to DND unless
1243       every file using that piece is DND */
1244    firstPieceDND = dnd;
1245    for( i=fileIndex-1; firstPieceDND && i>=0; --i ) {
1246        if( tor->info.files[i].lastPiece != firstPiece )
1247            break;
1248        firstPieceDND = tor->info.files[i].dnd;
1249    }
1250
1251    /* can't set the last piece to DND unless
1252       every file using that piece is DND */
1253    lastPieceDND = dnd;
1254    for( i=fileIndex+1; lastPieceDND && i<tor->info.fileCount; ++i ) {
1255        if( tor->info.files[i].firstPiece != lastPiece )
1256            break;
1257        lastPieceDND = tor->info.files[i].dnd;
1258    }
1259
1260    if( firstPiece == lastPiece )
1261    {
1262        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
1263    }
1264    else
1265    {
1266        tor->info.pieces[firstPiece].dnd = firstPieceDND;
1267        tor->info.pieces[lastPiece].dnd = lastPieceDND;
1268        for( i=firstPiece+1; i<lastPiece; ++i )
1269            tor->info.pieces[i].dnd = dnd;
1270    }
1271
1272    tr_cpInvalidateDND ( tor->completion );
1273
1274    saveFastResumeSoon( tor );
1275
1276    tr_torrentUnlock( tor );
1277}
1278
1279void
1280tr_torrentSetFileDLs ( tr_torrent  * tor,
1281                       int         * files,
1282                       int           fileCount,
1283                       int           doDownload )
1284{
1285    int i;
1286    for( i=0; i<fileCount; ++i )
1287        tr_torrentSetFileDL( tor, files[i], doDownload );
1288}
1289
1290/***
1291****
1292***/
1293
1294int _tr_block( const tr_torrent * tor, int index, int begin )
1295{
1296    const tr_info_t * inf = &tor->info;
1297    return index * ( inf->pieceSize / tor->blockSize ) +
1298        begin / tor->blockSize;
1299}
Note: See TracBrowser for help on using the repository browser.