#800 initial support for GetRight-style fetching of data through http and ftp servers specified in the .torrent's "url-list" tag

This commit is contained in:
Charles Kerr 2008-06-07 21:26:41 +00:00
parent e212eeceb7
commit e3e3bee8b5
19 changed files with 619 additions and 180 deletions

View File

@ -262,7 +262,7 @@ testing_port_begin( gpointer gdata )
const int port = gtk_spin_button_get_value_as_int( spin );
char url[256];
snprintf( url, sizeof(url), "http://portcheck.transmissionbt.com/%d", port );
tr_webRun( handle, url, testing_port_done, data );
tr_webRun( handle, url, NULL, testing_port_done, data );
}
return FALSE;
}
@ -492,7 +492,7 @@ onUpdateBlocklistCB( GtkButton * w, gpointer gdata )
g_signal_connect( d, "response", G_CALLBACK(onUpdateBlocklistResponseCB), data );
gtk_widget_show( d );
tr_webRun( handle, url, got_blocklist, data );
tr_webRun( handle, url, NULL, got_blocklist, data );
}
static GtkWidget*

View File

@ -42,7 +42,8 @@ libtransmission_a_SOURCES = \
upnp.c \
utils.c \
verify.c \
web.c
web.c \
webseed.c
noinst_HEADERS = \
bencode.h \
@ -63,6 +64,7 @@ noinst_HEADERS = \
metainfo.h \
natpmp.h \
net.h \
peer-common.h \
peer-io.h \
peer-mgr.h \
peer-mgr-private.h \
@ -84,7 +86,8 @@ noinst_HEADERS = \
upnp.h \
utils.h \
verify.h \
web.h
web.h \
webseed.h
TESTS = \
blocklist-test \

View File

@ -496,9 +496,8 @@ tr_bencInitRaw( tr_benc * val, const void * src, size_t byteCount )
{
tr_bencInit( val, TYPE_STR );
val->val.s.i = byteCount;
val->val.s.s = tr_new( char, byteCount );
val->val.s.s = tr_memdup( src, byteCount );
val->val.s.nofree = 0;
memcpy( val->val.s.s, src, byteCount );
}
int

View File

@ -99,12 +99,12 @@ compareOffsetToFile( const void * a, const void * b )
return 0;
}
static void
findFileLocation( const tr_torrent * tor,
tr_piece_index_t pieceIndex,
uint32_t pieceOffset,
tr_file_index_t * fileIndex,
uint64_t * fileOffset )
void
tr_ioFindFileLocation( const tr_torrent * tor,
tr_piece_index_t pieceIndex,
uint32_t pieceOffset,
tr_file_index_t * fileIndex,
uint64_t * fileOffset )
{
const uint64_t offset = tr_pieceOffset( tor, pieceIndex, pieceOffset, 0 );
const tr_file * file;
@ -172,8 +172,8 @@ readOrWritePiece( const tr_torrent * tor,
if( pieceOffset + buflen > tr_torPieceCountBytes( tor, pieceIndex ) )
return TR_ERROR_ASSERT;
findFileLocation ( tor, pieceIndex, pieceOffset,
&fileIndex, &fileOffset );
tr_ioFindFileLocation( tor, pieceIndex, pieceOffset,
&fileIndex, &fileOffset );
while( buflen && !err )
{

View File

@ -57,4 +57,15 @@ tr_errno tr_ioWrite( const struct tr_torrent * tor,
tr_errno tr_ioTestPiece( const tr_torrent*, int piece );
/**
* Converts a piece index + offset into a file index + offset.
*/
void tr_ioFindFileLocation( const tr_torrent * tor,
tr_piece_index_t pieceIndex,
uint32_t pieceOffset,
tr_file_index_t * fileIndex,
uint64_t * fileOffset );
#endif

View File

@ -223,6 +223,26 @@ announceToScrape( const char * announce )
return scrape;
}
static void
geturllist( tr_info * inf, tr_benc * meta )
{
benc_val_t * urls;
if( tr_bencDictFindList( meta, "url-list", &urls ) )
{
int i;
const char * url;
const int n = tr_bencListSize( urls );
inf->webseedCount = 0;
inf->webseeds = tr_new0( char*, n );
for( i=0; i<n; ++i )
if( tr_bencGetStr( tr_bencListChild( urls, i ), &url ) )
inf->webseeds[inf->webseedCount++] = tr_strdup( url );
}
}
static int
getannounce( tr_info * inf, tr_benc * meta )
{
@ -411,6 +431,9 @@ tr_metainfoParse( const tr_handle * handle,
if( getannounce( inf, meta ) )
goto fail;
/* get the url-list */
geturllist( inf, meta );
/* filename of Transmission's copy */
getTorrentFilename( handle, inf, buf, sizeof( buf ) );
tr_free( inf->torrent );
@ -428,9 +451,13 @@ void tr_metainfoFree( tr_info * inf )
tr_file_index_t ff;
int i;
for( i=0; i<inf->webseedCount; ++i )
tr_free( inf->webseeds[i] );
for( ff=0; ff<inf->fileCount; ++ff )
tr_free( inf->files[ff].name );
tr_free( inf->webseeds );
tr_free( inf->pieces );
tr_free( inf->files );
tr_free( inf->comment );

View File

@ -0,0 +1,59 @@
/*
* This file Copyright (C) 2008 Charles Kerr <charles@rebelbase.com>
*
* This file is licensed by the GPL version 2. Works owned by the
* Transmission project are granted a special exemption to clause 2(b)
* so that the bulk of its code can remain under the MIT license.
* This exemption does not extend to derived works not owned by
* the Transmission project.
*
* $Id$
*/
#ifndef TR_PEER_H
#define TR_PEER_H
/**
*** Fields common to webseed and bittorrent peers
**/
#include "transmission.h"
#include "publish.h"
enum
{
TR_ADDREQ_OK=0,
TR_ADDREQ_FULL,
TR_ADDREQ_DUPLICATE,
TR_ADDREQ_MISSING,
TR_ADDREQ_CLIENT_CHOKED
};
/**
*** Peer Publish / Subscribe
**/
typedef enum
{
TR_PEER_CLIENT_GOT_BLOCK,
TR_PEER_CLIENT_GOT_DATA,
TR_PEER_PEER_GOT_DATA,
TR_PEER_PEER_PROGRESS,
TR_PEER_ERROR,
TR_PEER_CANCEL,
TR_PEER_NEED_REQ
}
PeerEventType;
typedef struct
{
PeerEventType eventType;
uint32_t pieceIndex; /* for GOT_BLOCK */
uint32_t offset; /* for GOT_BLOCK */
uint32_t length; /* for GOT_BLOCK + GOT_DATA */
float progress; /* for TR_PEER_PEER_PROGRESS */
tr_errno err; /* for TR_PEER_GOT_ERROR */
}
tr_peer_event;
#endif

View File

@ -24,6 +24,7 @@
#include "completion.h"
#include "crypto.h"
#include "handshake.h"
#include "inout.h" /* tr_ioTestPiece */
#include "net.h"
#include "peer-io.h"
#include "peer-mgr.h"
@ -31,9 +32,11 @@
#include "peer-msgs.h"
#include "ptrarray.h"
#include "ratecontrol.h"
#include "stats.h" /* tr_statsAddDownloaded */
#include "torrent.h"
#include "trevent.h"
#include "utils.h"
#include "webseed.h"
enum
{
@ -103,6 +106,7 @@ typedef struct
tr_ptrArray * outgoingHandshakes; /* tr_handshake */
tr_ptrArray * pool; /* struct peer_atom */
tr_ptrArray * peers; /* tr_peer */
tr_ptrArray * webseeds; /* tr_webseed */
tr_timer * reconnectTimer;
tr_timer * rechokeTimer;
tr_timer * refillTimer;
@ -332,7 +336,7 @@ removePeer( Torrent * t, tr_peer * peer )
assert( atom != NULL );
atom->time = time( NULL );
removed = tr_ptrArrayRemoveSorted ( t->peers, peer, peerCompare );
removed = tr_ptrArrayRemoveSorted( t->peers, peer, peerCompare );
assert( removed == peer );
peerDestructor( removed );
}
@ -363,6 +367,7 @@ torrentDestructor( Torrent * t )
tr_timerFree( &t->refillTimer );
tr_bitfieldFree( t->requested );
tr_ptrArrayFree( t->webseeds, (PtrArrayForeachFunc)tr_webseedFree );
tr_ptrArrayFree( t->pool, (PtrArrayForeachFunc)tr_free );
tr_ptrArrayFree( t->outgoingHandshakes, NULL );
tr_ptrArrayFree( t->peers, NULL );
@ -370,9 +375,12 @@ torrentDestructor( Torrent * t )
tr_free( t );
}
static void peerCallbackFunc( void * vpeer, void * vevent, void * vt );
static Torrent*
torrentConstructor( tr_peerMgr * manager, tr_torrent * tor )
{
int i;
Torrent * t;
t = tr_new0( Torrent, 1 );
@ -380,10 +388,16 @@ torrentConstructor( tr_peerMgr * manager, tr_torrent * tor )
t->tor = tor;
t->pool = tr_ptrArrayNew( );
t->peers = tr_ptrArrayNew( );
t->webseeds = tr_ptrArrayNew( );
t->outgoingHandshakes = tr_ptrArrayNew( );
t->requested = tr_bitfieldNew( tor->blockCount );
memcpy( t->hash, tor->info.hash, SHA_DIGEST_LENGTH );
for( i=0; i<tor->info.webseedCount; ++i ) {
tr_webseed * w = tr_webseedNew( tor, tor->info.webseeds[i], peerCallbackFunc, t );
tr_ptrArrayAppend( t->webseeds, w );
}
return t;
}
@ -536,13 +550,18 @@ compareRefillPiece (const void * aIn, const void * bIn)
/* if one piece has a higher priority, it goes first */
if( a->priority != b->priority )
return a->priority > b->priority ? -1 : 1;
/* otherwise if one has fewer peers, it goes first */
if (a->peerCount != b->peerCount)
return a->peerCount < b->peerCount ? -1 : 1;
#if 0
/* otherwise download them in order */
return tr_compareUint16( a->piece, b->piece );
#else
/* otherwise go with our random seed */
return tr_compareUint16( a->random, b->random );
#endif
}
static int
@ -741,7 +760,9 @@ refillPulse( void * vtorrent )
tr_torrent * tor = t->tor;
tr_block_index_t i;
int peerCount;
int webseedCount;
tr_peer ** peers;
tr_webseed ** webseeds;
tr_block_index_t blockCount;
uint64_t * blocks;
@ -755,15 +776,23 @@ refillPulse( void * vtorrent )
blocks = getPreferredBlocks( t, &blockCount );
peers = getPeersUploadingToClient( t, &peerCount );
webseedCount = tr_ptrArraySize( t->webseeds );
webseeds = tr_memdup( tr_ptrArrayBase(t->webseeds), webseedCount*sizeof(tr_webseed*) );
for( i=0; peerCount && i<blockCount; ++i )
#warning do not check in this line
peerCount = 0;
for( i=0; (webseedCount || peerCount) && i<blockCount; ++i )
{
int j;
int handled = FALSE;
const tr_block_index_t block = blocks[i];
const tr_piece_index_t index = tr_torBlockPiece( tor, block );
const uint32_t begin = (block * tor->blockSize) - (index * tor->info.pieceSize);
const uint32_t length = tr_torBlockCountBytes( tor, block );
fprintf( stderr, "next block in the refill pulse is %d\n", (int)block );
assert( tr_torrentReqIsValid( tor, index, begin, length ) );
assert( _tr_block( tor, index, begin ) == block );
@ -771,7 +800,7 @@ refillPulse( void * vtorrent )
assert( (begin + length) <= tr_torPieceCountBytes( tor, index ) );
/* find a peer who can ask for this block */
for( j=0; j<peerCount; )
for( j=0; !handled && j<peerCount; )
{
const int val = tr_peerMsgsAddRequest( peers[j]->msgs, index, begin, length );
switch( val )
@ -780,17 +809,33 @@ refillPulse( void * vtorrent )
case TR_ADDREQ_CLIENT_CHOKED:
memmove( peers+j, peers+j+1, sizeof(tr_peer*)*(--peerCount-j) );
break;
case TR_ADDREQ_MISSING:
case TR_ADDREQ_DUPLICATE:
++j;
break;
case TR_ADDREQ_OK:
tr_bitfieldAdd( t->requested, block );
j = peerCount;
handled = TRUE;
break;
default:
assert( 0 && "unhandled value" );
break;
}
}
/* maybe one of the webseeds can do it */
for( j=0; !handled && j<webseedCount; )
{
const int val = tr_webseedAddRequest( webseeds[j], index, begin, length );
switch( val )
{
case TR_ADDREQ_FULL:
memmove( webseeds+j, webseeds+j+1, sizeof(tr_webseed*)*(--webseedCount-j) );
break;
case TR_ADDREQ_OK:
tr_bitfieldAdd( t->requested, block );
handled = TRUE;
break;
default:
assert( 0 && "unhandled value" );
break;
@ -799,6 +844,7 @@ refillPulse( void * vtorrent )
}
/* cleanup */
tr_free( webseeds );
tr_free( peers );
tr_free( blocks );
@ -807,20 +853,6 @@ refillPulse( void * vtorrent )
return FALSE;
}
static void
broadcastClientHave( Torrent * t, uint32_t index )
{
int i, size;
tr_peer ** peers;
assert( torrentIsLocked( t ) );
peers = getConnectedPeers( t, &size );
for( i=0; i<size; ++i )
tr_peerMsgsHave( peers[i]->msgs, index );
tr_free( peers );
}
static void
broadcastGotBlock( Torrent * t, uint32_t index, uint32_t offset, uint32_t length )
{
@ -850,55 +882,114 @@ addStrike( Torrent * t, tr_peer * peer )
}
static void
msgsCallbackFunc( void * vpeer, void * vevent, void * vt )
gotBadPiece( Torrent * t, tr_piece_index_t pieceIndex )
{
tr_peer * peer = vpeer;
tr_torrent * tor = t->tor;
const uint32_t byteCount = tr_torPieceCountBytes( tor, pieceIndex );
tor->corruptCur += byteCount;
tor->downloadedCur -= MIN( tor->downloadedCur, byteCount );
}
static void
peerCallbackFunc( void * vpeer, void * vevent, void * vt )
{
tr_peer * peer = vpeer; /* may be NULL if peer is a webseed */
Torrent * t = (Torrent *) vt;
const tr_peermsgs_event * e = (const tr_peermsgs_event *) vevent;
const tr_peer_event * e = vevent;
torrentLock( t );
switch( e->eventType )
{
case TR_PEERMSG_NEED_REQ:
case TR_PEER_NEED_REQ:
if( t->refillTimer == NULL )
t->refillTimer = tr_timerNew( t->manager->handle,
refillPulse, t,
REFILL_PERIOD_MSEC );
break;
case TR_PEERMSG_CANCEL:
case TR_PEER_CANCEL:
tr_bitfieldRem( t->requested, _tr_block( t->tor, e->pieceIndex, e->offset ) );
break;
case TR_PEERMSG_PIECE_DATA: {
struct peer_atom * atom = getExistingAtom( t, &peer->in_addr );
atom->piece_data_time = time( NULL );
case TR_PEER_PEER_GOT_DATA: {
const time_t now = time( NULL );
tr_torrent * tor = t->tor;
tor->activityDate = now;
tor->uploadedCur += e->length;
tr_rcTransferred( tor->upload, e->length );
tr_rcTransferred( tor->handle->upload, e->length );
tr_statsAddUploaded( tor->handle, e->length );
if( peer ) {
struct peer_atom * atom = getExistingAtom( t, &peer->in_addr );
atom->piece_data_time = time( NULL );
}
break;
}
case TR_PEERMSG_CLIENT_HAVE:
broadcastClientHave( t, e->pieceIndex );
tr_torrentRecheckCompleteness( t->tor );
case TR_PEER_CLIENT_GOT_DATA: {
const time_t now = time( NULL );
tr_torrent * tor = t->tor;
tor->activityDate = now;
tor->downloadedCur += e->length;
tr_rcTransferred( tor->download, e->length );
tr_rcTransferred( tor->handle->download, e->length );
tr_statsAddDownloaded( tor->handle, e->length );
if( peer ) {
struct peer_atom * atom = getExistingAtom( t, &peer->in_addr );
atom->piece_data_time = time( NULL );
}
break;
case TR_PEERMSG_PEER_PROGRESS: {
struct peer_atom * atom = getExistingAtom( t, &peer->in_addr );
const int peerIsSeed = e->progress >= 1.0;
if( peerIsSeed ) {
tordbg( t, "marking peer %s as a seed", tr_peerIoAddrStr(&atom->addr,atom->port) );
atom->flags |= ADDED_F_SEED_FLAG;
} else {
tordbg( t, "marking peer %s as a non-seed", tr_peerIoAddrStr(&atom->addr,atom->port) );
atom->flags &= ~ADDED_F_SEED_FLAG;
} break;
}
case TR_PEERMSG_CLIENT_BLOCK:
case TR_PEER_PEER_PROGRESS: {
if( peer ) {
struct peer_atom * atom = getExistingAtom( t, &peer->in_addr );
const int peerIsSeed = e->progress >= 1.0;
if( peerIsSeed ) {
tordbg( t, "marking peer %s as a seed", tr_peerIoAddrStr(&atom->addr,atom->port) );
atom->flags |= ADDED_F_SEED_FLAG;
} else {
tordbg( t, "marking peer %s as a non-seed", tr_peerIoAddrStr(&atom->addr,atom->port) );
atom->flags &= ~ADDED_F_SEED_FLAG;
}
}
break;
}
case TR_PEER_CLIENT_GOT_BLOCK:
{
tr_torrent * tor = t->tor;
tr_block_index_t block = _tr_block( tor, e->pieceIndex, e->offset );
tr_cpBlockAdd( tor->completion, block );
broadcastGotBlock( t, e->pieceIndex, e->offset, e->length );
break;
case TR_PEERMSG_ERROR:
if( tr_cpPieceIsComplete( tor->completion, e->pieceIndex ) )
{
const tr_piece_index_t p = e->pieceIndex;
const tr_errno err = tr_ioTestPiece( tor, p );
if( err ) {
tr_torerr( tor, _( "Piece %lu, which was just downloaded, failed its checksum test: %s" ),
(unsigned long)p, tr_errorString( err ) );
}
tr_torrentSetHasPiece( tor, p, !err );
tr_torrentSetPieceChecked( tor, p, TRUE );
tr_peerMgrSetBlame( tor->handle->peerMgr, tor->info.hash, p, !err );
if( err )
gotBadPiece( t, p );
tr_torrentRecheckCompleteness( tor );
}
break;
}
case TR_PEER_ERROR:
if( TR_ERROR_IS_IO( e->err ) ) {
t->tor->error = e->err;
tr_strlcpy( t->tor->errorString, tr_errorString( e->err ), sizeof(t->tor->errorString) );
@ -1024,7 +1115,7 @@ myHandshakeDoneCB( tr_handshake * handshake,
}
peer->port = port;
peer->io = io;
peer->msgs = tr_peerMsgsNew( t->tor, peer, msgsCallbackFunc, t, &peer->msgsTag );
peer->msgs = tr_peerMsgsNew( t->tor, peer, peerCallbackFunc, t, &peer->msgsTag );
atom->time = time( NULL );
}
}

View File

@ -155,10 +155,8 @@ reqListClear( struct request_list * list )
static void
reqListCopy( struct request_list * dest, const struct request_list * src )
{
dest->count = src->count;
dest->max = src->max;
dest->requests = tr_new( struct peer_request, dest->max );
memcpy( dest->requests, src->requests, sizeof( struct peer_request ) * dest->count );
dest->count = dest->max = src->count;
dest->requests = tr_memdup( src->requests, dest->count * sizeof( struct peer_request ) );
}
static void
@ -396,10 +394,10 @@ protocolSendChoke( tr_peermsgs * msgs, int choke )
*** EVENTS
**/
static const tr_peermsgs_event blankEvent = { 0, 0, 0, 0, 0.0f, 0 };
static const tr_peer_event blankEvent = { 0, 0, 0, 0, 0.0f, 0 };
static void
publish( tr_peermsgs * msgs, tr_peermsgs_event * e )
publish( tr_peermsgs * msgs, tr_peer_event * e )
{
tr_publisherPublish( msgs->publisher, msgs->info, e );
}
@ -407,8 +405,8 @@ publish( tr_peermsgs * msgs, tr_peermsgs_event * e )
static void
fireError( tr_peermsgs * msgs, tr_errno err )
{
tr_peermsgs_event e = blankEvent;
e.eventType = TR_PEERMSG_ERROR;
tr_peer_event e = blankEvent;
e.eventType = TR_PEER_ERROR;
e.err = err;
publish( msgs, &e );
}
@ -416,34 +414,25 @@ fireError( tr_peermsgs * msgs, tr_errno err )
static void
fireNeedReq( tr_peermsgs * msgs )
{
tr_peermsgs_event e = blankEvent;
e.eventType = TR_PEERMSG_NEED_REQ;
tr_peer_event e = blankEvent;
e.eventType = TR_PEER_NEED_REQ;
publish( msgs, &e );
}
static void
firePeerProgress( tr_peermsgs * msgs )
{
tr_peermsgs_event e = blankEvent;
e.eventType = TR_PEERMSG_PEER_PROGRESS;
tr_peer_event e = blankEvent;
e.eventType = TR_PEER_PEER_PROGRESS;
e.progress = msgs->info->progress;
publish( msgs, &e );
}
static void
fireClientHave( tr_peermsgs * msgs, uint32_t pieceIndex )
{
tr_peermsgs_event e = blankEvent;
e.eventType = TR_PEERMSG_CLIENT_HAVE;
e.pieceIndex = pieceIndex;
publish( msgs, &e );
}
static void
fireGotBlock( tr_peermsgs * msgs, const struct peer_request * req )
{
tr_peermsgs_event e = blankEvent;
e.eventType = TR_PEERMSG_CLIENT_BLOCK;
tr_peer_event e = blankEvent;
e.eventType = TR_PEER_CLIENT_GOT_BLOCK;
e.pieceIndex = req->index;
e.offset = req->offset;
e.length = req->length;
@ -451,18 +440,28 @@ fireGotBlock( tr_peermsgs * msgs, const struct peer_request * req )
}
static void
firePieceData( tr_peermsgs * msgs )
fireClientGotData( tr_peermsgs * msgs, uint32_t length )
{
tr_peermsgs_event e = blankEvent;
e.eventType = TR_PEERMSG_PIECE_DATA;
tr_peer_event e = blankEvent;
e.length = length;
e.eventType = TR_PEER_CLIENT_GOT_DATA;
publish( msgs, &e );
}
static void
firePeerGotData( tr_peermsgs * msgs, uint32_t length )
{
tr_peer_event e = blankEvent;
e.length = length;
e.eventType = TR_PEER_PEER_GOT_DATA;
publish( msgs, &e );
}
static void
fireCancelledReq( tr_peermsgs * msgs, const struct peer_request * req )
{
tr_peermsgs_event e = blankEvent;
e.eventType = TR_PEERMSG_CANCEL;
tr_peer_event e = blankEvent;
e.eventType = TR_PEER_CANCEL;
e.pieceIndex = req->index;
e.offset = req->offset;
e.length = req->length;
@ -1186,16 +1185,9 @@ clientGotBlock( tr_peermsgs * msgs, const uint8_t * block, const struct peer_req
static void
clientGotBytes( tr_peermsgs * msgs, uint32_t byteCount )
{
const time_t now = time( NULL );
tr_torrent * tor = msgs->torrent;
tor->activityDate = now;
tor->downloadedCur += byteCount;
msgs->info->pieceDataActivityDate = now;
msgs->info->pieceDataActivityDate = time( NULL );
tr_rcTransferred( msgs->info->rcToClient, byteCount );
tr_rcTransferred( tor->download, byteCount );
tr_rcTransferred( tor->handle->download, byteCount );
tr_statsAddDownloaded( msgs->handle, byteCount );
firePieceData( msgs );
fireClientGotData( msgs, byteCount );
}
static int
@ -1408,16 +1400,9 @@ readBtMessage( tr_peermsgs * msgs, struct evbuffer * inbuf, size_t inlen )
static void
peerGotBytes( tr_peermsgs * msgs, uint32_t byteCount )
{
const time_t now = time( NULL );
tr_torrent * tor = msgs->torrent;
tor->activityDate = now;
tor->uploadedCur += byteCount;
msgs->info->pieceDataActivityDate = now;
msgs->info->pieceDataActivityDate = time( NULL );
tr_rcTransferred( msgs->info->rcToPeer, byteCount );
tr_rcTransferred( tor->upload, byteCount );
tr_rcTransferred( tor->handle->upload, byteCount );
tr_statsAddUploaded( msgs->handle, byteCount );
firePieceData( msgs );
firePeerGotData( msgs, byteCount );
}
static size_t
@ -1443,24 +1428,6 @@ decrementDownloadedCount( tr_peermsgs * msgs, uint32_t byteCount )
tor->downloadedCur -= MIN( tor->downloadedCur, byteCount );
}
static void
reassignBytesToCorrupt( tr_peermsgs * msgs, uint32_t byteCount )
{
tr_torrent * tor = msgs->torrent;
tor->corruptCur += byteCount;
decrementDownloadedCount( msgs, byteCount );
}
static void
gotBadPiece( tr_peermsgs * msgs, tr_piece_index_t pieceIndex )
{
const uint32_t byteCount =
tr_torPieceCountBytes( msgs->torrent, pieceIndex );
reassignBytesToCorrupt( msgs, byteCount );
}
static void
clientGotUnwantedBlock( tr_peermsgs * msgs, const struct peer_request * req )
{
@ -1529,35 +1496,8 @@ clientGotBlock( tr_peermsgs * msgs,
if(( err = tr_ioWrite( tor, req->index, req->offset, req->length, data )))
return err;
tr_cpBlockAdd( tor->completion, block );
addPeerToBlamefield( msgs, req->index );
fireGotBlock( msgs, req );
/**
*** Handle if this was the last block in the piece
**/
if( tr_cpPieceIsComplete( tor->completion, req->index ) )
{
const tr_errno err = tr_ioTestPiece( tor, req->index );
if( err )
tr_torerr( tor, _( "Piece %lu, which was just downloaded, failed its checksum test: %s" ),
(unsigned long)req->index,
tr_errorString( err ) );
tr_torrentSetHasPiece( tor, req->index, !err );
tr_torrentSetPieceChecked( tor, req->index, TRUE );
tr_peerMgrSetBlame( tor->handle->peerMgr, tor->info.hash, req->index, !err );
if( !err )
fireClientHave( msgs, req->index );
else
gotBadPiece( msgs, req->index );
}
return 0;
}

View File

@ -14,6 +14,7 @@
#define TR_PEER_MSGS_H
#include <inttypes.h>
#include "peer-common.h"
#include "publish.h"
struct tr_torrent;
@ -42,6 +43,7 @@ void tr_peerMsgsCancel( tr_peermsgs * msgs,
void tr_peerMsgsFree( tr_peermsgs* );
#if 0
enum {
TR_ADDREQ_OK=0,
TR_ADDREQ_FULL,
@ -49,6 +51,7 @@ enum {
TR_ADDREQ_MISSING,
TR_ADDREQ_CLIENT_CHOKED
};
#endif
int tr_peerMsgsAddRequest( tr_peermsgs * peer,
uint32_t index,
@ -59,6 +62,7 @@ int tr_peerMsgsAddRequest( tr_peermsgs * peer,
*** PeerMsgs Publish / Subscribe
**/
#if 0
typedef enum
{
TR_PEERMSG_CLIENT_HAVE,
@ -81,12 +85,9 @@ typedef struct
tr_errno err; /* for TR_PEERMSG_GOT_ERROR */
}
tr_peermsgs_event;
#endif
tr_publisher_tag tr_peerMsgsSubscribe ( tr_peermsgs * peer,
tr_delivery_func func,
void * user );
void tr_peerMsgsUnsubscribe ( tr_peermsgs * peer,
tr_publisher_tag tag );
void tr_peerMsgsUnsubscribe ( tr_peermsgs * peer,
tr_publisher_tag tag );
#endif

View File

@ -57,10 +57,8 @@ tr_ptrArrayDup( tr_ptrArray* in )
tr_ptrArray * out;
out = tr_new( tr_ptrArray, 1 );
out->n_items = in->n_items;
out->n_alloc = in->n_items;
out->items = tr_new( void*, out->n_alloc );
memcpy( out->items, in->items, out->n_items * sizeof(void*) );
out->n_items = out->n_alloc = in->n_items;
out->items = tr_memdup( in->items, out->n_items * sizeof(void*) );
return out;
}
@ -98,6 +96,12 @@ tr_ptrArrayPeek( tr_ptrArray * t, int * size )
return t->items;
}
void**
tr_ptrArrayBase( tr_ptrArray * t )
{
return t->items;
}
void*
tr_ptrArrayNth( tr_ptrArray* t, int i )
{

View File

@ -696,7 +696,6 @@ static void
invokeRequest( void * vreq )
{
struct tr_tracker_request * req = vreq;
uint8_t * hash;
tr_tracker * t = findTracker( req->session, req->torrent_hash );
if( t )
@ -720,9 +719,8 @@ invokeRequest( void * vreq )
++req->session->tracker->runningCount;
hash = tr_new0( uint8_t, SHA_DIGEST_LENGTH );
memcpy( hash, req->torrent_hash, SHA_DIGEST_LENGTH );
tr_webRun( req->session, req->url, req->done_func, hash );
tr_webRun( req->session, req->url, NULL, req->done_func,
tr_memdup( req->torrent_hash, SHA_DIGEST_LENGTH ) );
freeRequest( req );
}

View File

@ -1036,6 +1036,9 @@ struct tr_info
tr_tracker_info * trackers;
int trackerCount;
char ** webseeds;
int webseedCount;
/* Torrent info */
char * comment;
char * creator;

View File

@ -238,7 +238,7 @@ tr_msg( const char * file, int line, int level,
newmsg = tr_new0( tr_msg_list, 1 );
newmsg->level = level;
newmsg->when = time( NULL );
newmsg->message = tr_strdup( (char*)EVBUFFER_DATA( buf ) );
newmsg->message = tr_strdup( EVBUFFER_DATA( buf ) );
newmsg->file = file;
newmsg->line = line;
newmsg->name = tr_strdup( name );
@ -597,13 +597,13 @@ tr_memdup( const void * in, int byteCount )
}
char*
tr_strdup( const char * in )
tr_strdup( const void * in )
{
return tr_strndup( in, in ? strlen(in) : 0 );
return tr_strndup( in, in ? strlen((const char*)in) : 0 );
}
char*
tr_strndup( const char * in, int len )
tr_strndup( const void * in, int len )
{
char * out = NULL;

View File

@ -188,8 +188,8 @@ void* tr_malloc0 ( size_t ) TR_GNUC_MALLOC;
void* tr_calloc ( size_t nmemb, size_t size ) TR_GNUC_MALLOC;
void tr_free ( void* );
char* tr_strdup( const char * str ) TR_GNUC_MALLOC;
char* tr_strndup( const char * str, int len ) TR_GNUC_MALLOC;
char* tr_strdup( const void * str ) TR_GNUC_MALLOC;
char* tr_strndup( const void * str, int len ) TR_GNUC_MALLOC;
void* tr_memdup( const void * src, int byteCount ) TR_GNUC_MALLOC;
char* tr_strdup_printf( const char * fmt, ... ) TR_GNUC_PRINTF( 1, 2 ) TR_GNUC_MALLOC;
char* tr_base64_encode( const void * input, int inlen, int *outlen ) TR_GNUC_MALLOC;

View File

@ -46,6 +46,7 @@ struct tr_web_task
unsigned long tag;
struct evbuffer * response;
char * url;
char * range;
tr_session * session;
tr_web_done_func * done_func;
void * done_func_user_data;
@ -86,6 +87,7 @@ processCompletedTasks( tr_web * web )
curl_multi_remove_handle( web->cm, easy );
curl_easy_cleanup( easy );
evbuffer_free( task->response );
tr_free( task->range );
tr_free( task->url );
tr_free( task );
}
@ -154,7 +156,10 @@ addTask( void * vtask )
curl_easy_setopt( ch, CURLOPT_FOLLOWLOCATION, 1 );
curl_easy_setopt( ch, CURLOPT_MAXREDIRS, 5 );
curl_easy_setopt( ch, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );
curl_easy_setopt( ch, CURLOPT_ENCODING, "" );
if( task->range )
curl_easy_setopt( ch, CURLOPT_RANGE, task->range );
else /* don't set encoding if range is sent; it messes up binary data */
curl_easy_setopt( ch, CURLOPT_ENCODING, "" );
curl_multi_add_handle( web->cm, ch );
}
}
@ -162,6 +167,7 @@ addTask( void * vtask )
void
tr_webRun( tr_session * session,
const char * url,
const char * range,
tr_web_done_func * done_func,
void * done_func_user_data )
{
@ -173,6 +179,7 @@ tr_webRun( tr_session * session,
task = tr_new0( struct tr_web_task, 1 );
task->session = session;
task->url = tr_strdup( url );
task->range = tr_strdup( range );
task->done_func = done_func;
task->done_func_user_data = done_func_user_data;
task->tag = ++tag;

View File

@ -16,20 +16,21 @@
struct tr_handle;
typedef struct tr_web tr_web;
tr_web* tr_webInit( tr_handle * session );
tr_web* tr_webInit( struct tr_handle * session );
void tr_webClose( tr_web ** );
typedef void (tr_web_done_func)( tr_handle * session,
long response_code,
const void * response,
size_t response_byte_count,
void * user_data );
typedef void (tr_web_done_func)( struct tr_handle * session,
long response_code,
const void * response,
size_t response_byte_count,
void * user_data );
const char * tr_webGetResponseStr( long response_code );
void tr_webRun( tr_handle * session,
void tr_webRun( struct tr_handle * session,
const char * url,
const char * range,
tr_web_done_func done_func,
void * done_func_user_data );

263
libtransmission/webseed.c Normal file
View File

@ -0,0 +1,263 @@
/*
* This file Copyright (C) 2008 Charles Kerr <charles@rebelbase.com>
*
* This file is licensed by the GPL version 2. Works owned by the
* Transmission project are granted a special exemption to clause 2(b)
* so that the bulk of its code can remain under the MIT license.
* This exemption does not extend to derived works not owned by
* the Transmission project.
*
* $Id$
*/
#include <assert.h>
#include <string.h> /* strlen */
#include <event.h>
#include "transmission.h"
#include "inout.h"
#include "list.h"
#include "torrent.h"
#include "utils.h"
#include "web.h"
#include "webseed.h"
struct tr_webseed
{
unsigned int busy : 1;
unsigned int dead : 1;
tr_torrent * torrent;
char * url;
tr_delivery_func * callback;
void * callback_userdata;
tr_piece_index_t pieceIndex;
uint32_t pieceOffset;
uint32_t byteCount;
struct evbuffer * content;
};
/***
****
***/
static const tr_peer_event blankEvent = { 0, 0, 0, 0, 0.0f, 0 };
static void
publish( tr_webseed * w, tr_peer_event * e )
{
if( w->callback )
w->callback( NULL, e, w->callback_userdata );
}
static void
fireNeedReq( tr_webseed * w )
{
tr_peer_event e = blankEvent;
e.eventType = TR_PEER_NEED_REQ;
publish( w, &e );
}
static void
fireClientGotBlock( tr_webseed * w, uint32_t pieceIndex, uint32_t offset, uint32_t length )
{
tr_peer_event e = blankEvent;
e.eventType = TR_PEER_CLIENT_GOT_BLOCK;
e.pieceIndex = pieceIndex;
e.offset = offset;
e.length = length;
publish( w, &e );
}
static void
fireClientGotData( tr_webseed * w, uint32_t length )
{
tr_peer_event e = blankEvent;
e.eventType = TR_PEER_CLIENT_GOT_DATA;
e.length = length;
publish( w, &e );
}
/***
****
***/
static char*
makeURL( tr_webseed * w, const tr_file * file )
{
char * ret;
struct evbuffer * out = evbuffer_new( );
const char * url = w->url;
const size_t url_len = strlen( url );
evbuffer_add( out, url, url_len );
/* if url ends with a '/', add the torrent name */
if( url[url_len-1] == '/' )
{
const char * str = file->name;
/* this is like curl_escape() but doesn't munge the
* '/' directory separators in the path */
while( str && *str )
{
switch( *str ) {
case ',': case '-': case '.': case '/':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case 'a': case 'b': case 'c': case 'd': case 'e':
case 'f': case 'g': case 'h': case 'i': case 'j':
case 'k': case 'l': case 'm': case 'n': case 'o':
case 'p': case 'q': case 'r': case 's': case 't':
case 'u': case 'v': case 'w': case 'x': case 'y': case 'z':
case 'A': case 'B': case 'C': case 'D': case 'E':
case 'F': case 'G': case 'H': case 'I': case 'J':
case 'K': case 'L': case 'M': case 'N': case 'O':
case 'P': case 'Q': case 'R': case 'S': case 'T':
case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
evbuffer_add( out, str, 1 );
break;
default:
evbuffer_add_printf( out, "%%%02X", *str );
break;
}
}
}
ret = tr_strndup( EVBUFFER_DATA( out ), EVBUFFER_LENGTH( out ) );
evbuffer_free( out );
return ret;
}
static void requestNextChunk( tr_webseed * w );
static void
webResponseFunc( tr_handle * session UNUSED,
long response_code,
const void * response,
size_t response_byte_count,
void * vw )
{
tr_webseed * w = vw;
const int success = ( response_code == 206 );
fprintf( stderr, "server responded with code %ld and %lu bytes\n", response_code, (unsigned long)response_byte_count );
if( !success )
{
/* FIXME */
}
else
{
evbuffer_add( w->content, response, response_byte_count );
if( !w->dead )
fireClientGotData( w, response_byte_count );
if( EVBUFFER_LENGTH( w->content ) < w->byteCount )
requestNextChunk( w );
else {
tr_ioWrite( w->torrent, w->pieceIndex, w->pieceOffset, w->byteCount, EVBUFFER_DATA(w->content) );
evbuffer_drain( w->content, EVBUFFER_LENGTH( w->content ) );
w->busy = 0;
if( w->dead )
tr_webseedFree( w );
else {
fireClientGotBlock( w, w->pieceIndex, w->pieceOffset, w->byteCount );
fireNeedReq( w );
}
}
}
}
static void
requestNextChunk( tr_webseed * w )
{
const tr_info * inf = tr_torrentInfo( w->torrent );
const uint32_t have = EVBUFFER_LENGTH( w->content );
const uint32_t left = w->byteCount - have;
const uint32_t pieceOffset = w->pieceOffset + have;
tr_file_index_t fileIndex;
uint64_t fileOffset;
uint32_t thisPass;
char * url;
char * range;
tr_ioFindFileLocation( w->torrent, w->pieceIndex, pieceOffset,
&fileIndex, &fileOffset );
thisPass = MIN( left, inf->files[fileIndex].length - fileOffset );
url = makeURL( w, &inf->files[fileIndex] );
//fprintf( stderr, "url is [%s]\n", url );
range = tr_strdup_printf( "%"PRIu64"-%"PRIu64, fileOffset, fileOffset + thisPass - 1 );
fprintf( stderr, "range is [%s] ... we want %lu total, we have %lu, so %lu are left, and we're asking for %lu this time\n", range, (unsigned long)w->byteCount, (unsigned long)have, (unsigned long)left, (unsigned long)thisPass );
tr_webRun( w->torrent->handle, url, range, webResponseFunc, w );
tr_free( range );
tr_free( url );
}
int
tr_webseedAddRequest( tr_webseed * w,
uint32_t pieceIndex,
uint32_t pieceOffset,
uint32_t byteCount )
{
int ret;
if( w->busy || w->dead )
{
ret = TR_ADDREQ_FULL;
}
else
{
fprintf( stderr, "webseed is starting a new piece here -- piece %lu, offset %lu!!!\n", (unsigned long)pieceIndex, (unsigned long)pieceOffset );
w->busy = 1;
w->pieceIndex = pieceIndex;
w->pieceOffset = pieceOffset;
w->byteCount = byteCount;
evbuffer_drain( w->content, EVBUFFER_LENGTH( w->content ) );
requestNextChunk( w );
ret = TR_ADDREQ_OK;
}
return ret;
}
/***
****
***/
tr_webseed*
tr_webseedNew( struct tr_torrent * torrent,
const char * url,
tr_delivery_func callback,
void * callback_userdata )
{
tr_webseed * w = tr_new0( tr_webseed, 1 );
w->content = evbuffer_new( );
w->torrent = torrent;
w->url = tr_strdup( url );
w->callback = callback;
w->callback_userdata = callback_userdata;
return w;
}
void
tr_webseedFree( tr_webseed * w )
{
if( w )
{
if( w->busy )
{
w->dead = 1;
}
else
{
evbuffer_free( w->content );
tr_free( w->url );
tr_free( w );
}
}
}

32
libtransmission/webseed.h Normal file
View File

@ -0,0 +1,32 @@
/*
* This file Copyright (C) 2008 Charles Kerr <charles@rebelbase.com>
*
* This file is licensed by the GPL version 2. Works owned by the
* Transmission project are granted a special exemption to clause 2(b)
* so that the bulk of its code can remain under the MIT license.
* This exemption does not extend to derived works not owned by
* the Transmission project.
*
* $Id$
*/
#ifndef TR_WEBSEED_H
#define TR_WEBSEED_H
typedef struct tr_webseed tr_webseed;
#include "peer-common.h"
tr_webseed* tr_webseedNew( struct tr_torrent * torrent,
const char * url,
tr_delivery_func delivery_func,
void * delivery_userdata );
void tr_webseedFree( tr_webseed * );
int tr_webseedAddRequest( tr_webseed * w,
uint32_t index,
uint32_t begin,
uint32_t length );
#endif