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:
parent
17b9dc7462
commit
6aaa2fd39e
|
@ -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 \
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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 );
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue