2008-06-07 21:26:41 +00:00
/*
* 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 )
2008-09-23 19:11:04 +00:00
* so that the bulk of its code can remain under the MIT license .
2008-06-07 21:26:41 +00:00
* 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"
2008-06-10 01:38:12 +00:00
# include "ratecontrol.h"
2008-06-07 21:26:41 +00:00
# include "torrent.h"
# include "utils.h"
# include "web.h"
# include "webseed.h"
struct tr_webseed
{
2008-10-11 04:07:50 +00:00
unsigned int busy : 1 ;
2008-06-07 21:26:41 +00:00
unsigned int dead : 1 ;
2008-11-08 22:24:07 +00:00
uint8_t hash [ SHA_DIGEST_LENGTH ] ;
2008-06-07 21:26:41 +00:00
2008-11-08 22:24:07 +00:00
char * url ;
tr_delivery_func * callback ;
2008-09-23 19:11:04 +00:00
void * callback_userdata ;
2008-06-07 21:26:41 +00:00
2008-10-11 04:07:50 +00:00
tr_piece_index_t pieceIndex ;
uint32_t pieceOffset ;
uint32_t byteCount ;
2008-06-07 21:26:41 +00:00
2008-11-08 22:24:07 +00:00
tr_ratecontrol * rateDown ;
tr_session * session ;
2008-06-10 01:38:12 +00:00
2008-11-08 22:24:07 +00:00
struct evbuffer * content ;
2008-06-07 21:26:41 +00:00
} ;
/***
* * * *
* * */
static const tr_peer_event blankEvent = { 0 , 0 , 0 , 0 , 0.0f , 0 } ;
static void
2008-09-23 19:11:04 +00:00
publish ( tr_webseed * w ,
tr_peer_event * e )
2008-06-07 21:26:41 +00:00
{
if ( w - > callback )
w - > callback ( NULL , e , w - > callback_userdata ) ;
}
static void
fireNeedReq ( tr_webseed * w )
{
tr_peer_event e = blankEvent ;
2008-09-23 19:11:04 +00:00
2008-06-07 21:26:41 +00:00
e . eventType = TR_PEER_NEED_REQ ;
publish ( w , & e ) ;
}
static void
2008-09-23 19:11:04 +00:00
fireClientGotBlock ( tr_webseed * w ,
uint32_t pieceIndex ,
uint32_t offset ,
uint32_t length )
2008-06-07 21:26:41 +00:00
{
tr_peer_event e = blankEvent ;
2008-09-23 19:11:04 +00:00
2008-06-07 21:26:41 +00:00
e . eventType = TR_PEER_CLIENT_GOT_BLOCK ;
e . pieceIndex = pieceIndex ;
e . offset = offset ;
e . length = length ;
publish ( w , & e ) ;
}
static void
2008-09-23 19:11:04 +00:00
fireClientGotData ( tr_webseed * w ,
uint32_t length )
2008-06-07 21:26:41 +00:00
{
tr_peer_event e = blankEvent ;
2008-09-23 19:11:04 +00:00
2008-06-07 21:26:41 +00:00
e . eventType = TR_PEER_CLIENT_GOT_DATA ;
e . length = length ;
publish ( w , & e ) ;
}
/***
* * * *
* * */
static char *
2008-09-23 19:11:04 +00:00
makeURL ( tr_webseed * w ,
const tr_file * file )
2008-06-07 21:26:41 +00:00
{
2008-09-23 19:11:04 +00:00
char * ret ;
2008-06-07 21:26:41 +00:00
struct evbuffer * out = evbuffer_new ( ) ;
2008-09-23 19:11:04 +00:00
const char * url = w - > url ;
const size_t url_len = strlen ( url ) ;
2008-06-07 21:26:41 +00:00
evbuffer_add ( out , url , url_len ) ;
/* if url ends with a '/', add the torrent name */
2008-09-23 19:11:04 +00:00
if ( url [ url_len - 1 ] = = ' / ' )
2008-06-07 21:26:41 +00:00
{
const char * str = file - > name ;
2008-09-23 19:11:04 +00:00
/* this is like curl_escape() but doesn't munge the
2008-06-07 21:26:41 +00:00
* ' / ' directory separators in the path */
while ( str & & * str )
{
2008-09-23 19:11:04 +00:00
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 ' :
2008-06-07 21:26:41 +00:00
evbuffer_add ( out , str , 1 ) ;
break ;
2008-09-23 19:11:04 +00:00
2008-06-07 21:26:41 +00:00
default :
evbuffer_add_printf ( out , " %%%02X " , * str ) ;
break ;
}
2008-09-16 07:10:04 +00:00
str + + ;
2008-06-07 21:26:41 +00:00
}
}
ret = tr_strndup ( EVBUFFER_DATA ( out ) , EVBUFFER_LENGTH ( out ) ) ;
evbuffer_free ( out ) ;
return ret ;
}
static void requestNextChunk ( tr_webseed * w ) ;
static void
2008-11-08 22:24:07 +00:00
webResponseFunc ( tr_handle * session ,
long response_code ,
const void * response ,
size_t response_byte_count ,
void * vw )
2008-06-07 21:26:41 +00:00
{
tr_webseed * w = vw ;
2008-11-08 22:24:07 +00:00
tr_torrent * tor = tr_torrentFindFromHash ( session , w - > hash ) ;
2008-09-23 19:11:04 +00:00
const int success = ( response_code = = 206 ) ;
2008-06-07 21:26:41 +00:00
2008-09-23 19:11:04 +00:00
/*fprintf( stderr, "server responded with code %ld and %lu bytes\n",
response_code , ( unsigned long ) response_byte_count ) ; */
2008-06-07 21:26:41 +00:00
if ( ! success )
{
/* FIXME */
}
2008-11-08 22:24:07 +00:00
else if ( tor ! = NULL )
2008-06-07 21:26:41 +00:00
{
evbuffer_add ( w - > content , response , response_byte_count ) ;
2008-10-11 04:07:50 +00:00
if ( ! w - > dead )
2008-06-09 22:53:45 +00:00
{
2008-10-11 04:07:50 +00:00
fireClientGotData ( w , response_byte_count ) ;
tr_rcTransferred ( w - > rateDown , response_byte_count ) ;
2008-06-09 22:53:45 +00:00
}
2008-10-11 04:07:50 +00:00
if ( EVBUFFER_LENGTH ( w - > content ) < w - > byteCount )
2008-06-07 21:26:41 +00:00
requestNextChunk ( w ) ;
2008-10-11 04:07:50 +00:00
else {
2008-11-08 22:24:07 +00:00
tr_ioWrite ( tor , w - > pieceIndex , w - > pieceOffset , w - > byteCount , EVBUFFER_DATA ( w - > content ) ) ;
2008-06-07 21:26:41 +00:00
evbuffer_drain ( w - > content , EVBUFFER_LENGTH ( w - > content ) ) ;
2008-10-11 04:07:50 +00:00
w - > busy = 0 ;
if ( w - > dead )
tr_webseedFree ( w ) ;
else {
fireClientGotBlock ( w , w - > pieceIndex , w - > pieceOffset , w - > byteCount ) ;
2008-06-07 21:26:41 +00:00
fireNeedReq ( w ) ;
2008-10-11 04:07:50 +00:00
}
2008-06-07 21:26:41 +00:00
}
}
}
static void
requestNextChunk ( tr_webseed * w )
{
2008-11-08 22:24:07 +00:00
tr_torrent * tor = tr_torrentFindFromHash ( w - > session , w - > hash ) ;
if ( tor ! = NULL )
{
const tr_info * inf = tr_torrentInfo ( tor ) ;
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 ( tor , w - > pieceIndex , pieceOffset ,
& fileIndex , & fileOffset ) ;
thisPass = MIN ( left , inf - > files [ fileIndex ] . length - fileOffset ) ;
url = makeURL ( w , & inf - > files [ fileIndex ] ) ;
2008-10-27 18:09:15 +00:00
/*fprintf( stderr, "url is [%s]\n", url );*/
2008-11-08 22:24:07 +00:00
range = tr_strdup_printf ( " % " PRIu64 " -% " PRIu64 , fileOffset , fileOffset + thisPass - 1 ) ;
2008-10-27 18:09:15 +00:00
/*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 );*/
2008-11-08 22:24:07 +00:00
tr_webRun ( w - > session , url , range , webResponseFunc , w ) ;
tr_free ( range ) ;
tr_free ( url ) ;
}
2008-06-07 21:26:41 +00:00
}
2008-08-22 16:30:07 +00:00
tr_addreq_t
2008-10-11 04:07:50 +00:00
tr_webseedAddRequest ( tr_webseed * w ,
uint32_t pieceIndex ,
uint32_t pieceOffset ,
uint32_t byteCount )
2008-06-07 21:26:41 +00:00
{
int ret ;
2008-10-11 04:07:50 +00:00
if ( w - > busy | | w - > dead )
2008-06-07 21:26:41 +00:00
{
ret = TR_ADDREQ_FULL ;
}
else
{
2008-10-11 04:07:50 +00:00
w - > busy = 1 ;
w - > pieceIndex = pieceIndex ;
w - > pieceOffset = pieceOffset ;
w - > byteCount = byteCount ;
evbuffer_drain ( w - > content , EVBUFFER_LENGTH ( w - > content ) ) ;
requestNextChunk ( w ) ;
2008-06-07 21:26:41 +00:00
ret = TR_ADDREQ_OK ;
}
return ret ;
}
2008-06-10 02:36:52 +00:00
int
tr_webseedIsActive ( const tr_webseed * w )
{
2008-10-11 04:07:50 +00:00
return w - > busy ! = 0 ;
2008-06-10 02:36:52 +00:00
}
2008-06-10 01:38:12 +00:00
int
2008-09-23 19:11:04 +00:00
tr_webseedGetSpeed ( const tr_webseed * w ,
float * setme_KiBs )
2008-06-10 01:38:12 +00:00
{
2008-06-10 02:36:52 +00:00
const int isActive = tr_webseedIsActive ( w ) ;
2008-09-23 19:11:04 +00:00
2008-06-10 02:36:52 +00:00
* setme_KiBs = isActive ? tr_rcRate ( w - > rateDown ) : 0.0f ;
return isActive ;
2008-06-10 01:38:12 +00:00
}
2008-06-07 21:26:41 +00:00
/***
* * * *
* * */
tr_webseed *
tr_webseedNew ( struct tr_torrent * torrent ,
2008-09-23 19:11:04 +00:00
const char * url ,
2008-06-07 21:26:41 +00:00
tr_delivery_func callback ,
2008-09-23 19:11:04 +00:00
void * callback_userdata )
2008-06-07 21:26:41 +00:00
{
tr_webseed * w = tr_new0 ( tr_webseed , 1 ) ;
2008-09-23 19:11:04 +00:00
2008-11-08 22:24:07 +00:00
memcpy ( w - > hash , torrent - > info . hash , SHA_DIGEST_LENGTH ) ;
w - > session = torrent - > session ;
2008-06-07 21:26:41 +00:00
w - > content = evbuffer_new ( ) ;
2008-06-10 01:38:12 +00:00
w - > rateDown = tr_rcInit ( ) ;
2008-06-07 21:26:41 +00:00
w - > url = tr_strdup ( url ) ;
w - > callback = callback ;
w - > callback_userdata = callback_userdata ;
2008-06-09 22:53:45 +00:00
/*fprintf( stderr, "w->callback_userdata is %p\n", w->callback_userdata );*/
2008-06-07 21:26:41 +00:00
return w ;
}
void
tr_webseedFree ( tr_webseed * w )
{
if ( w )
{
2008-10-11 04:07:50 +00:00
if ( w - > busy )
2008-06-07 21:26:41 +00:00
{
w - > dead = 1 ;
}
else
{
evbuffer_free ( w - > content ) ;
2008-06-10 01:38:12 +00:00
tr_rcClose ( w - > rateDown ) ;
2008-06-07 21:26:41 +00:00
tr_free ( w - > url ) ;
tr_free ( w ) ;
}
}
}