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:
parent
77233ab16f
commit
7beacb3032
13 changed files with 148 additions and 172 deletions
|
@ -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 );
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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]));
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
43
libtransmission/utils-test.c
Normal file
43
libtransmission/utils-test.c
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue