diff --git a/libtransmission/Makefile.am b/libtransmission/Makefile.am index 6d3a0bc9b..93bc52fa6 100644 --- a/libtransmission/Makefile.am +++ b/libtransmission/Makefile.am @@ -58,6 +58,7 @@ noinst_HEADERS = \ handshake.h \ inout.h \ ipcparse.h \ + json.h \ list.h \ makemeta.h \ metainfo.h \ diff --git a/libtransmission/bencode-test.c b/libtransmission/bencode-test.c index 7f14f4f6e..a4dbb417a 100644 --- a/libtransmission/bencode-test.c +++ b/libtransmission/bencode-test.c @@ -1,6 +1,7 @@ #include #include "transmission.h" #include "bencode.h" +#include "json.h" #include "utils.h" /* tr_free */ #define VERBOSE 0 @@ -270,6 +271,34 @@ 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; + + return 0; +} + static int testJSONSnippet( const char * benc_str, const char * expected ) { @@ -277,6 +306,10 @@ testJSONSnippet( const char * benc_str, const char * expected ) char * serialized; tr_bencLoad( benc_str, strlen( benc_str ), &top, NULL ); serialized = tr_bencSaveAsJSON( &top, NULL ); +#if 0 +fprintf( stderr, " expected: [%s]\n", expected ); +fprintf( stderr, " got: [%s]\n", serialized ); +#endif check( !strcmp( serialized, expected ) ); tr_free( serialized ); tr_bencFree( &top ); @@ -310,6 +343,11 @@ testJSON( void ) if(( val = testJSONSnippet( benc_str, expected ))) return val; + benc_str = "d4:argsd6:statuslee6:result7:successe"; + expected = "{ \"args\": { \"status\": [ ] }, \"result\": \"success\" }"; + if(( val = testJSONSnippet( benc_str, expected ))) + return val; + return 0; } @@ -362,6 +400,9 @@ main( void ) if(( i = testJSON( ))) return i; + if(( i = testRISON( ))) + return i; + if(( i = testStackSmash( ))) return i; diff --git a/libtransmission/bencode.c b/libtransmission/bencode.c index 300429b35..266c22339 100644 --- a/libtransmission/bencode.c +++ b/libtransmission/bencode.c @@ -1036,18 +1036,14 @@ jsonChildFunc( struct jsonWalk * data ) const int i = parentState->childIndex++; if( ! ( i % 2 ) ) evbuffer_add_printf( data->out, ": " ); - else if( parentState->childIndex < parentState->childCount ) - evbuffer_add_printf( data->out, ", " ); else - evbuffer_add_printf( data->out, " }" ); + evbuffer_add_printf( data->out, ", " ); break; } case TYPE_LIST: { - if( ++parentState->childIndex < parentState->childCount ) - evbuffer_add_printf( data->out, ", " ); - else - evbuffer_add_printf( data->out, " ]" ); + ++parentState->childIndex; + evbuffer_add_printf( data->out, ", " ); break; } @@ -1109,6 +1105,8 @@ jsonDictBeginFunc( const tr_benc * val, void * vdata ) struct jsonWalk * data = vdata; jsonPushParent( data, val ); evbuffer_add_printf( data->out, "{ " ); + if( !val->val.l.count ) + evbuffer_add_printf( data->out, " " ); } static void jsonListBeginFunc( const tr_benc * val, void * vdata ) @@ -1116,11 +1114,18 @@ jsonListBeginFunc( const tr_benc * val, void * vdata ) struct jsonWalk * data = vdata; jsonPushParent( data, val ); evbuffer_add_printf( data->out, "[ " ); + if( !val->val.l.count ) + evbuffer_add_printf( data->out, " " ); } static void -jsonContainerEndFunc( const tr_benc * val UNUSED, void * vdata ) +jsonContainerEndFunc( const tr_benc * val, void * vdata ) { struct jsonWalk * data = vdata; + EVBUFFER_LENGTH( data->out ) -= 2; + if( tr_bencIsDict( val ) ) + evbuffer_add_printf( data->out, " }" ); + else /* list */ + evbuffer_add_printf( data->out, " ]" ); jsonPopParent( data ); jsonChildFunc( data ); } diff --git a/libtransmission/json.c b/libtransmission/json.c index 007c43e20..aab24964a 100644 --- a/libtransmission/json.c +++ b/libtransmission/json.c @@ -11,11 +11,12 @@ */ #include -//#include /* memcpy, memcmp, strstr */ -//#include /* qsort */ +#include +#include #include /* printf */ -//#include /* INT_MAX */ -// + +#include /* evbuffer */ + #include "JSON_checker.h" #include "transmission.h" @@ -145,3 +146,121 @@ 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 = 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; +} diff --git a/libtransmission/json.h b/libtransmission/json.h new file mode 100644 index 000000000..2ba1dd1d9 --- /dev/null +++ b/libtransmission/json.h @@ -0,0 +1,23 @@ +/* + * This file Copyright (C) 2008 Charles Kerr + * + * 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_JSON_H + +int tr_jsonParse( const void * vbuf, + const void * bufend, + struct tr_benc * setme_benc, + const uint8_t ** setme_end ); + +char* tr_rison2json( const char * rison, + int rison_len ); + +#endif diff --git a/libtransmission/rpc.c b/libtransmission/rpc.c index 874bbb858..50064b34e 100644 --- a/libtransmission/rpc.c +++ b/libtransmission/rpc.c @@ -16,6 +16,7 @@ #include "transmission.h" #include "bencode.h" #include "rpc.h" +#include "json.h" #include "torrent.h" #include "utils.h" @@ -604,21 +605,42 @@ request_exec( struct tr_handle * handle, if( !result ) result = "success"; tr_bencDictAddStr( &response, "result", result ); +fprintf( stderr, "response [%s]", tr_bencSave( &response, NULL ) ); +tr_bencPrint( &response ); out = tr_bencSaveAsJSON( &response, response_len ); tr_bencFree( &response ); return out; } char* -tr_rpc_request_exec( struct tr_handle * handle, - const void * request_json, - int request_len, - int * response_len ) +tr_rpc_request_exec_json( struct tr_handle * handle, + const void * request_json, + int request_len, + int * response_len ) { tr_benc top; - int have_content = !tr_jsonParse( request_json, (const char*)request_json + request_len, &top, NULL ); - char * ret = request_exec( handle, have_content ? &top : NULL, response_len ); + int have_content; + char * ret; + + if( request_len < 0 ) + request_len = strlen( request_json ); + + have_content = !tr_jsonParse( request_json, (const char*)request_json + request_len, &top, NULL ); + ret = request_exec( handle, have_content ? &top : NULL, response_len ); + if( have_content ) tr_bencFree( &top ); return ret; } + +char* +tr_rpc_request_exec_rison( struct tr_handle * handle, + const void * request_rison, + int request_len, + int * response_len ) +{ + char * json = tr_rison2json( request_rison, request_len ); + char * ret = tr_rpc_request_exec_json( handle, json, -1, response_len ); + tr_free( json ); + return ret; +} diff --git a/libtransmission/rpc.h b/libtransmission/rpc.h index 5b26a059c..bff5bfe39 100644 --- a/libtransmission/rpc.h +++ b/libtransmission/rpc.h @@ -15,10 +15,18 @@ struct tr_handle; +/* http://www.json.org/ */ char* -tr_rpc_request_exec( struct tr_handle * handle, - const void * request_json, - int request_len, - int * response_len ); +tr_rpc_request_exec_json( struct tr_handle * handle, + const void * request_json, + int request_len, + int * response_len ); + +/* http://mjtemplate.org/examples/rison.html */ +char* +tr_rpc_request_exec_rison( struct tr_handle * handle, + const void * request_rison, + int request_len, + int * response_len ); #endif