2008-06-07 21:26:41 +00:00
/*
2008-12-16 00:20:44 +00:00
* This file Copyright ( C ) 2008 Charles Kerr < charles @ transmissionbt . com >
2008-06-07 21:26:41 +00:00
*
* 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-11-28 22:11:41 +00:00
tr_bool busy ;
tr_bool dead ;
2008-06-07 21:26:41 +00:00
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
2009-01-02 20:42:35 +00:00
tr_ratecontrol rateDown ;
2008-11-08 22:24:07 +00:00
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
} ;
/***
* * * *
* * */
2008-12-02 19:46:51 +00:00
static const tr_peer_event blankEvent = { 0 , 0 , 0 , 0 , 0.0f , 0 , 0 , 0 } ;
2008-06-07 21:26:41 +00:00
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 ;
e . eventType = TR_PEER_NEED_REQ ;
publish ( w , & e ) ;
}
static void
2008-12-05 22:56:19 +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 ;
e . eventType = TR_PEER_CLIENT_GOT_BLOCK ;
e . pieceIndex = pieceIndex ;
e . offset = offset ;
e . length = length ;
publish ( w , & e ) ;
}
static void
2008-12-05 22:56:19 +00:00
fireClientGotData ( tr_webseed * w , uint32_t length )
2008-06-07 21:26:41 +00:00
{
tr_peer_event e = blankEvent ;
e . eventType = TR_PEER_CLIENT_GOT_DATA ;
e . length = length ;
2008-11-17 04:00:57 +00:00
e . wasPieceData = TRUE ;
2008-06-07 21:26:41 +00:00
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-12-30 20:32:00 +00:00
struct evbuffer * out = tr_getBuffer ( ) ;
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 )
{
2008-12-05 22:56:19 +00:00
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 ;
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 ) ) ;
2008-12-30 20:32:00 +00:00
tr_releaseBuffer ( out ) ;
2008-06-07 21:26:41 +00:00
return ret ;
}
static void requestNextChunk ( tr_webseed * w ) ;
static void
2008-12-14 11:21:11 +00:00
webResponseFunc ( tr_session * session ,
2008-11-08 22:24:07 +00:00
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 ) ;
2009-01-02 20:42:35 +00:00
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-12-05 22:56:19 +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
2009-01-02 20:42:35 +00:00
* setme_KiBs = isActive ? tr_rcRate ( & w - > rateDown ) : 0.0f ;
2008-06-10 02:36:52 +00:00
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 ( ) ;
w - > url = tr_strdup ( url ) ;
w - > callback = callback ;
w - > callback_userdata = callback_userdata ;
2009-01-02 20:42:35 +00:00
tr_rcConstruct ( & w - > rateDown ) ;
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 ) ;
2009-01-02 20:42:35 +00:00
tr_rcDestruct ( & w - > rateDown ) ;
2008-06-07 21:26:41 +00:00
tr_free ( w - > url ) ;
tr_free ( w ) ;
}
}
}