1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2024-12-24 08:43:27 +00:00

(1) RPC "add-torrent" now lets clients embed base64-encoded metainfo directly into the request

(2) remove the RISON code; it didn't make the final cut
(3) add base64 encode/decode utilities and unit tests
This commit is contained in:
Charles Kerr 2008-05-20 17:33:54 +00:00
parent 77233ab16f
commit 7beacb3032
13 changed files with 148 additions and 172 deletions

View file

@ -100,6 +100,16 @@ absolutify( char * buf, size_t len, const char * path )
return buf;
}
static char*
getEncodedMetainfo( const char * filename )
{
size_t len;
uint8_t * buf = tr_loadFile( filename, &len );
char * b64 = tr_base64_encode( buf, len, NULL );
tr_free( buf );
return b64;
}
static void
readargs( int argc, char ** argv )
{
@ -132,17 +142,19 @@ readargs( int argc, char ** argv )
while((( opt = getopt_long( argc, argv, optstr, longopts, NULL ))) != -1 )
{
char * tmp;
char buf[MAX_PATH_LENGTH];
tr_benc top, *args;
tr_bencInitDict( &top, 3 );
args = tr_bencDictAddDict( &top, "args", 0 );
args = tr_bencDictAddDict( &top, "arguments", 0 );
switch( opt )
{
case 'g': debug = 1; break;
case 'h': showUsage( ); break;
case 'a': tr_bencDictAddStr( &top, "method", "torrent-add" );
tr_bencDictAddStr( args, "filename", optarg );
tr_bencDictAddStr( args, "metainfo", ((tmp=getEncodedMetainfo(optarg))) );
tr_free( tmp );
break;
case 'c': tr_bencDictAddStr( &top, "method", "session-set" );
tr_bencDictAddStr( args, "encryption", optarg );

View file

@ -158,10 +158,12 @@
-------------------+-------------------------------------------------
"download-dir" | string path to download the torrent to
"filename" | string location of the .torrent file
"metainfo" | string base64-encoded .torrent content
"paused" | boolean if true, don't start the torrent
"peer-limit" | int maximum number of peers
The "filename" argument is required; all others are optional.
Either "filename" OR "metainfo" must be included.
All other arguments are optional.
Response arguments: on success, a "torrent-added" object in the
form of one of 3.3's tr_info objects.

View file

@ -385,6 +385,7 @@ static void parse_buffer_push_back_char(JSON_checker jc, char c)
jc->parse_buffer_capacity *= 2;
if (jc->parse_buffer == &jc->static_parse_buffer[0]) {
jc->parse_buffer = (char*)malloc(jc->parse_buffer_capacity * sizeof(jc->parse_buffer[0]));
memcpy( jc->parse_buffer, jc->static_parse_buffer, jc->parse_buffer_count );
} else {
jc->parse_buffer = (char*)realloc(jc->parse_buffer, jc->parse_buffer_capacity * sizeof(jc->parse_buffer[0]));
}

View file

@ -4,12 +4,11 @@ AM_CFLAGS = $(OPENSSL_CFLAGS) $(LIBCURL_CFLAGS) $(PTHREAD_CFLAGS)
noinst_LIBRARIES = libtransmission.a
libtransmission_a_SOURCES = \
ConvertUTF.c \
JSON_checker.c \
bencode.c \
blocklist.c \
clients.c \
completion.c \
ConvertUTF.c \
crypto.c \
fastresume.c \
fdlimit.c \
@ -17,6 +16,7 @@ libtransmission_a_SOURCES = \
handshake.c \
inout.c \
json.c \
JSON_checker.c \
list.c \
makemeta.c \
metainfo.c \
@ -45,11 +45,10 @@ libtransmission_a_SOURCES = \
web.c
noinst_HEADERS = \
ConvertUTF.h \
JSON_checker.h \
bencode.h \
blocklist.h \
clients.h \
ConvertUTF.h \
crypto.h \
completion.h \
fastresume.h \
@ -58,6 +57,7 @@ noinst_HEADERS = \
handshake.h \
inout.h \
json.h \
JSON_checker.h \
list.h \
makemeta.h \
metainfo.h \
@ -92,7 +92,8 @@ TESTS = \
clients-test \
json-test \
test-fastset \
test-peer-id
test-peer-id \
utils-test
noinst_PROGRAMS = $(TESTS)
@ -120,6 +121,8 @@ test_fastset_SOURCES = test-fastset.c
test_fastset_LDADD = $(APPS_LDADD)
test_peer_id_SOURCES = test-peer-id.c
test_peer_id_LDADD = $(APPS_LDADD)
utils_test_SOURCES = utils-test.c
utils_test_LDADD = $(APPS_LDADD)
clean-local:

View file

@ -271,39 +271,6 @@ testParse( void )
return 0;
}
static int
testRISONSnippet( const char * rison_str, const char * expected )
{
char * serialized = tr_rison2json( rison_str, -1 );
#if 0
fprintf( stderr, " expected: [%s]\n", expected );
fprintf( stderr, " got: [%s]\n", serialized );
#endif
check( !strcmp( serialized, expected ) );
tr_free( serialized );
return 0;
}
static int
testRISON( void )
{
int val;
const char * rison;
const char * expected;
rison = "(a:0,b:foo,c:'23skidoo')";
expected = "{ \"a\": 0, \"b\": \"foo\", \"c\": \"23skidoo\" }";
if(( val = testRISONSnippet( rison, expected )))
return val;
rison = "(method:torrent-info)";
expected = "{ \"method\": \"torrent-info\" }";
if(( val = testRISONSnippet( rison, expected )))
return val;
return 0;
}
static int
testJSONSnippet( const char * benc_str, const char * expected )
{
@ -405,9 +372,6 @@ main( void )
if(( i = testJSON( )))
return i;
if(( i = testRISON( )))
return i;
if(( i = testStackSmash( )))
return i;

View file

@ -90,7 +90,6 @@ static inline void tr_bencInit( tr_benc * val, int type )
void _tr_bencInitStr( tr_benc * val, char * str, int len, int nofree );
int tr_bencInitStrDup( tr_benc * val, const char * str );
void tr_bencInitRaw( tr_benc * val, const void * src, size_t byteCount );
int tr_bencInitStrDupLen( tr_benc * val, const char * str, int len );
void tr_bencInitInt( tr_benc * val, int64_t num );
int tr_bencInitDict( tr_benc * val, int reserveCount );
int tr_bencInitList( tr_benc * val, int reserveCount );

View file

@ -147,122 +147,3 @@ tr_jsonParse( const void * vbuf,
tr_ptrArrayFree( data.stack, NULL );
return err;
}
/***
**** RISON-to-JSON converter
***/
enum { ESCAPE,
STRING_BEGIN,
STRING, ESCAPE_STRING,
UNQUOTED_STRING, ESCAPE_UNQUOTED_STRING,
VAL_BEGIN,
OTHER };
char*
tr_rison2json( const char * str, int rison_len )
{
struct evbuffer * out = evbuffer_new( );
int stack[1000], *parents=stack;
int mode = OTHER;
char * ret;
const char * end;
if( rison_len < 0 )
end = str + strlen( str );
else
end = str + rison_len;
#define IN_OBJECT ((parents!=stack) && (parents[-1]=='}'))
for( ; str!=end; ++str )
{
if( mode == ESCAPE )
{
switch( *str )
{
case '(': evbuffer_add_printf( out, "[ " ); *parents++ = ']'; break;
case 't': evbuffer_add_printf( out, " true" ); break;
case 'f': evbuffer_add_printf( out, " false" ); break;
case 'n': evbuffer_add_printf( out, " null" ); break;
default: fprintf( stderr, "invalid escape sequence!\n" ); break;
}
mode = OTHER;
}
else if( mode == STRING_BEGIN )
{
switch( *str )
{
case '\'': evbuffer_add_printf( out, "\"" ); mode = STRING; break;
case ')': evbuffer_add_printf( out, " %c", *--parents ); mode = OTHER; break;
default: evbuffer_add_printf( out, "\"%c", *str ); mode = UNQUOTED_STRING; break;
}
}
else if( mode == UNQUOTED_STRING )
{
switch( *str )
{
case '\'': evbuffer_add_printf( out, "\"" ); mode = OTHER; break;
case ':': evbuffer_add_printf( out, "\": "); mode = VAL_BEGIN; break;
case '!': mode = ESCAPE_UNQUOTED_STRING; break;
case ')': if( IN_OBJECT ) { evbuffer_add_printf( out, "\" }"); mode = OTHER; break; }
case ',': if( IN_OBJECT ) { evbuffer_add_printf( out, "\", "); mode = STRING_BEGIN; break; }
/* fallthrough */
default: evbuffer_add_printf( out, "%c", *str ); break;
}
}
else if( mode == VAL_BEGIN )
{
if( *str == '\'' ) { evbuffer_add_printf( out, "\"" ); mode = STRING; }
else if( isdigit( *str ) ) { evbuffer_add_printf( out, "%c", *str ); mode = OTHER; }
else { evbuffer_add_printf( out, "\"%c", *str ); mode = UNQUOTED_STRING; }
}
else if( mode == STRING )
{
switch( *str )
{
case '\'': evbuffer_add_printf( out, "\"" ); mode = OTHER; break;
case '!': mode = ESCAPE_STRING; break;
default: evbuffer_add_printf( out, "%c", *str ); break;
}
}
else if( mode == ESCAPE_STRING || mode == ESCAPE_UNQUOTED_STRING )
{
switch( *str )
{
case '!': evbuffer_add_printf( out, "!" ); break;
case '\'': evbuffer_add_printf( out, "'" ); break;
default: fprintf( stderr, "invalid string escape sequence\n" ); break;
}
if( mode == ESCAPE_UNQUOTED_STRING ) mode = UNQUOTED_STRING;
if( mode == ESCAPE_STRING ) mode = STRING;
}
else
{
switch( *str )
{
case '(': evbuffer_add_printf( out, "{ " ); mode=STRING_BEGIN; *parents++ = '}'; break;
case '!': mode = ESCAPE; break;
case ')': evbuffer_add_printf( out, " %c", *--parents ); break;
case '\'': evbuffer_add_printf( out, "\"" ); mode = STRING; break;
case ':': if( IN_OBJECT ) {
evbuffer_add_printf( out, ": " ); mode = VAL_BEGIN;
} else {
evbuffer_add_printf( out, "%c", *str );
}
break;
case ',': if( IN_OBJECT ) {
evbuffer_add_printf( out, ", " ); mode = STRING_BEGIN;
} else {
evbuffer_add_printf( out, "%c", *str );
}
break;
default: evbuffer_add_printf( out, "%c", *str ); break;
}
}
}
ret = tr_strdup( (char*) EVBUFFER_DATA( out ) );
evbuffer_free( out );
return ret;
}

View file

@ -17,7 +17,4 @@ int tr_jsonParse( const void * vbuf,
struct tr_benc * setme_benc,
const uint8_t ** setme_end );
char* tr_rison2json( const char * rison,
int rison_len );
#endif

View file

@ -66,7 +66,7 @@ handle_rpc( struct shttpd_arg * arg )
}
evbuffer_add_printf( s->out, "HTTP/1.1 200 OK\r\n"
"Content-Type: text/x-json\r\n"
"Content-Type: application/json\r\n"
"Content-Length: %d\r\n"
"\r\n"
"%*.*s\r\n", len, len, len, response );

View file

@ -527,9 +527,12 @@ torrentSetPriorities( tr_handle * h,
static const char*
torrentAdd( tr_handle * h, tr_benc * args_in, tr_benc * args_out )
{
const char * filename;
if( !tr_bencDictFindStr( args_in, "filename", &filename ) )
return "no filename specified";
const char * filename = NULL;
const char * metainfo_base64 = NULL;
tr_bencDictFindStr( args_in, "filename", &filename );
tr_bencDictFindStr( args_in, "metainfo", &metainfo_base64 );
if( !filename && !metainfo_base64 )
return "no filename or metainfo specified";
else
{
int64_t i;
@ -539,13 +542,25 @@ torrentAdd( tr_handle * h, tr_benc * args_in, tr_benc * args_out )
tr_torrent * tor;
ctor = tr_ctorNew( h );
tr_ctorSetMetainfoFromFile( ctor, filename );
/* set the metainfo */
if( filename )
tr_ctorSetMetainfoFromFile( ctor, filename );
else {
int len;
char * metainfo = tr_base64_decode( metainfo_base64, -1, &len );
tr_ctorSetMetainfo( ctor, (uint8_t*)metainfo, len );
tr_free( metainfo );
}
/* set the optional arguments */
if( tr_bencDictFindStr( args_in, "download-dir", &str ) )
tr_ctorSetDownloadDir( ctor, TR_FORCE, str );
if( tr_bencDictFindInt( args_in, "paused", &i ) )
tr_ctorSetPaused( ctor, TR_FORCE, i );
if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
tr_ctorSetPeerLimit( ctor, TR_FORCE, i );
tor = tr_torrentNew( h, ctor, &err );
tr_ctorFree( ctor );

View file

@ -0,0 +1,43 @@
#include <stdio.h> /* fprintf */
#include <string.h> /* strcmp */
#include "transmission.h"
#include "utils.h"
#define VERBOSE 0
int test = 0;
#define check(A) { \
++test; \
if (A) { \
if( VERBOSE ) \
fprintf( stderr, "PASS test #%d (%s, %d)\n", test, __FILE__, __LINE__ ); \
} else { \
if( VERBOSE ) \
fprintf( stderr, "FAIL test #%d (%s, %d)\n", test, __FILE__, __LINE__ ); \
return test; \
} \
}
int
main( void )
{
char *in, *out;
int len;
/* base64 */
in = "YOYO!";
out = tr_base64_encode( in, -1, &len );
check( out );
check( !strcmp( out, "WU9ZTyE=\n" ) );
check( len == 9 );
in = tr_base64_decode( out, -1, &len );
check( in );
check( !strcmp( in, "YOYO!" ) );
check( len == 5 );
tr_free( in );
tr_free( out );
return 0;
}

View file

@ -601,6 +601,7 @@ tr_strndup( const char * in, int len )
memcpy( out, in, len );
out[len] = '\0';
}
return out;
}
@ -1014,3 +1015,58 @@ tr_httpParseURL( const char * url_in, int len,
tr_free( tmp );
return err;
}
#include <string.h>
#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>
char *
tr_base64_encode( const void * input, int length, int * setme_len )
{
char * ret;
BIO * b64;
BIO * bmem;
BUF_MEM * bptr;
if( length < 1 )
length = strlen( input );
bmem = BIO_new( BIO_s_mem( ) );
b64 = BIO_new( BIO_f_base64( ) );
b64 = BIO_push( b64, bmem );
BIO_write( b64, input, length );
(void) BIO_flush( b64 );
BIO_get_mem_ptr( b64, &bptr );
ret = tr_strndup( bptr->data, bptr->length );
if( setme_len )
*setme_len = bptr->length;
BIO_free_all( b64 );
return ret;
}
char *
tr_base64_decode( const void * input, int length, int * setme_len )
{
char * ret;
BIO * b64;
BIO * bmem;
int retlen;
if( length < 1 )
length = strlen( input );
ret = tr_new0( char, length );
b64 = BIO_new( BIO_f_base64( ) );
bmem = BIO_new_mem_buf( (unsigned char*)input, length );
bmem = BIO_push( b64, bmem );
retlen = BIO_read( bmem, ret, length );
if( setme_len )
*setme_len = retlen;
BIO_free_all( bmem );
return ret;
}

View file

@ -191,6 +191,9 @@ 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_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;
char* tr_base64_decode( const void * input, int inlen, int *outlen ) TR_GNUC_MALLOC;
size_t tr_strlcpy( char * dst, const char * src, size_t siz );