fix bug in the benc-to-json converter.

add rison-to-json converter so we can pass commands to transmission in a uri.
add unit tests for the new code.
This commit is contained in:
Charles Kerr 2008-05-13 12:52:58 +00:00
parent 17b9dc7462
commit 6aaa2fd39e
7 changed files with 241 additions and 22 deletions

View File

@ -58,6 +58,7 @@ noinst_HEADERS = \
handshake.h \ handshake.h \
inout.h \ inout.h \
ipcparse.h \ ipcparse.h \
json.h \
list.h \ list.h \
makemeta.h \ makemeta.h \
metainfo.h \ metainfo.h \

View File

@ -1,6 +1,7 @@
#include <stdio.h> #include <stdio.h>
#include "transmission.h" #include "transmission.h"
#include "bencode.h" #include "bencode.h"
#include "json.h"
#include "utils.h" /* tr_free */ #include "utils.h" /* tr_free */
#define VERBOSE 0 #define VERBOSE 0
@ -270,6 +271,34 @@ testParse( void )
return 0; 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 static int
testJSONSnippet( const char * benc_str, const char * expected ) testJSONSnippet( const char * benc_str, const char * expected )
{ {
@ -277,6 +306,10 @@ testJSONSnippet( const char * benc_str, const char * expected )
char * serialized; char * serialized;
tr_bencLoad( benc_str, strlen( benc_str ), &top, NULL ); tr_bencLoad( benc_str, strlen( benc_str ), &top, NULL );
serialized = tr_bencSaveAsJSON( &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 ) ); check( !strcmp( serialized, expected ) );
tr_free( serialized ); tr_free( serialized );
tr_bencFree( &top ); tr_bencFree( &top );
@ -310,6 +343,11 @@ testJSON( void )
if(( val = testJSONSnippet( benc_str, expected ))) if(( val = testJSONSnippet( benc_str, expected )))
return val; return val;
benc_str = "d4:argsd6:statuslee6:result7:successe";
expected = "{ \"args\": { \"status\": [ ] }, \"result\": \"success\" }";
if(( val = testJSONSnippet( benc_str, expected )))
return val;
return 0; return 0;
} }
@ -362,6 +400,9 @@ main( void )
if(( i = testJSON( ))) if(( i = testJSON( )))
return i; return i;
if(( i = testRISON( )))
return i;
if(( i = testStackSmash( ))) if(( i = testStackSmash( )))
return i; return i;

View File

@ -1036,18 +1036,14 @@ jsonChildFunc( struct jsonWalk * data )
const int i = parentState->childIndex++; const int i = parentState->childIndex++;
if( ! ( i % 2 ) ) if( ! ( i % 2 ) )
evbuffer_add_printf( data->out, ": " ); evbuffer_add_printf( data->out, ": " );
else if( parentState->childIndex < parentState->childCount )
evbuffer_add_printf( data->out, ", " );
else else
evbuffer_add_printf( data->out, " }" ); evbuffer_add_printf( data->out, ", " );
break; break;
} }
case TYPE_LIST: { case TYPE_LIST: {
if( ++parentState->childIndex < parentState->childCount ) ++parentState->childIndex;
evbuffer_add_printf( data->out, ", " ); evbuffer_add_printf( data->out, ", " );
else
evbuffer_add_printf( data->out, " ]" );
break; break;
} }
@ -1109,6 +1105,8 @@ jsonDictBeginFunc( const tr_benc * val, void * vdata )
struct jsonWalk * data = vdata; struct jsonWalk * data = vdata;
jsonPushParent( data, val ); jsonPushParent( data, val );
evbuffer_add_printf( data->out, "{ " ); evbuffer_add_printf( data->out, "{ " );
if( !val->val.l.count )
evbuffer_add_printf( data->out, " " );
} }
static void static void
jsonListBeginFunc( const tr_benc * val, void * vdata ) jsonListBeginFunc( const tr_benc * val, void * vdata )
@ -1116,11 +1114,18 @@ jsonListBeginFunc( const tr_benc * val, void * vdata )
struct jsonWalk * data = vdata; struct jsonWalk * data = vdata;
jsonPushParent( data, val ); jsonPushParent( data, val );
evbuffer_add_printf( data->out, "[ " ); evbuffer_add_printf( data->out, "[ " );
if( !val->val.l.count )
evbuffer_add_printf( data->out, " " );
} }
static void static void
jsonContainerEndFunc( const tr_benc * val UNUSED, void * vdata ) jsonContainerEndFunc( const tr_benc * val, void * vdata )
{ {
struct jsonWalk * data = 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 ); jsonPopParent( data );
jsonChildFunc( data ); jsonChildFunc( data );
} }

View File

@ -11,11 +11,12 @@
*/ */
#include <assert.h> #include <assert.h>
//#include <string.h> /* memcpy, memcmp, strstr */ #include <ctype.h>
//#include <stdlib.h> /* qsort */ #include <string.h>
#include <stdio.h> /* printf */ #include <stdio.h> /* printf */
//#include <limits.h> /* INT_MAX */
// #include <event.h> /* evbuffer */
#include "JSON_checker.h" #include "JSON_checker.h"
#include "transmission.h" #include "transmission.h"
@ -145,3 +146,121 @@ tr_jsonParse( const void * vbuf,
tr_ptrArrayFree( data.stack, NULL ); tr_ptrArrayFree( data.stack, NULL );
return err; 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;
}

23
libtransmission/json.h Normal file
View File

@ -0,0 +1,23 @@
/*
* 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)
* 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

View File

@ -16,6 +16,7 @@
#include "transmission.h" #include "transmission.h"
#include "bencode.h" #include "bencode.h"
#include "rpc.h" #include "rpc.h"
#include "json.h"
#include "torrent.h" #include "torrent.h"
#include "utils.h" #include "utils.h"
@ -604,21 +605,42 @@ request_exec( struct tr_handle * handle,
if( !result ) if( !result )
result = "success"; result = "success";
tr_bencDictAddStr( &response, "result", result ); tr_bencDictAddStr( &response, "result", result );
fprintf( stderr, "response [%s]", tr_bencSave( &response, NULL ) );
tr_bencPrint( &response );
out = tr_bencSaveAsJSON( &response, response_len ); out = tr_bencSaveAsJSON( &response, response_len );
tr_bencFree( &response ); tr_bencFree( &response );
return out; return out;
} }
char* char*
tr_rpc_request_exec( struct tr_handle * handle, tr_rpc_request_exec_json( struct tr_handle * handle,
const void * request_json, const void * request_json,
int request_len, int request_len,
int * response_len ) int * response_len )
{ {
tr_benc top; tr_benc top;
int have_content = !tr_jsonParse( request_json, (const char*)request_json + request_len, &top, NULL ); int have_content;
char * ret = request_exec( handle, have_content ? &top : NULL, response_len ); 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 ) if( have_content )
tr_bencFree( &top ); tr_bencFree( &top );
return ret; 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;
}

View File

@ -15,10 +15,18 @@
struct tr_handle; struct tr_handle;
/* http://www.json.org/ */
char* char*
tr_rpc_request_exec( struct tr_handle * handle, tr_rpc_request_exec_json( struct tr_handle * handle,
const void * request_json, const void * request_json,
int request_len, int request_len,
int * response_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 #endif