source: trunk/libtransmission/torrent.c @ 3379

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

Add an option for saving a torrent's destination path in its file. This can be overridden by clients with the `destination' argument in tr_torrentInit*()

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