diff --git a/doc/rpc-json-spec.txt b/doc/rpc-json-spec.txt index 4327fbf94..c919ee372 100644 --- a/doc/rpc-json-spec.txt +++ b/doc/rpc-json-spec.txt @@ -7,18 +7,18 @@ The JSON terminology in RFC 4627 is used. "array" is equivalent to a benc list; "object" is equivalent to a benc dictionary; - an object's "strings" are the dictionary's keys, - and an object's "members" are its string/value pairs. + an object's "keys" are the dictionary's string keys, + and an object's "members" are its key/value pairs. 2. Message Format Messages are formatted in a subset of JSON that understands arrays, maps, strings, and whole numbers with no exponentials -- - in short, the subset of JSON easily represented in benc. - floating-point numbers are represented as strings. - booleans are represented as integers where 0 is false and 1 is true. + in short, the subset of JSON easily represented as bencoded data. + Floating-point numbers are represented as strings. + Booleans are represented as integers where 0 is false and 1 is true. - Messages are represented as a JSON objects. There are two types: + Messages are represented as JSON objects. There are two types: requests (described in 2.1) and responses (described in 2.2). 2.1. Requests @@ -46,12 +46,12 @@ Method names: "torrent-start", "torrent-stop", "torrent-remove", "torrent-verify" - Request arguments: "ids", a list of unique torrent ids, sha1 hash strings, + Request arguments: "ids", a list of torrent id integers, sha1 hash strings, or both. These are the torrents that the request will be applied to. If "ids" is ommitted, the request is applied to all torrents. - Response arguments: none. + Response arguments: none 3.2. Torrent Info Requests @@ -71,13 +71,13 @@ { "arguments": { "ids": [ 7, 10 ] } "method": "torrent-info", - "tag": 666 + "tag": 39693 } Example Response: { - "tag": 666 + "tag": 39693 "result": "success", "arguments": { "info": [ @@ -108,11 +108,11 @@ Request arguments: 3.1's optional "ids" argument. Response arguments: "status", an array of objects based on - libtransmission's tr_stat struct but which differ the following ways: + libtransmission's tr_stat struct but which differ in the following ways: - (1) tr_stat's "tracker" field is omitted - (2) a new string, "announce-url", is added - (3) a new string, "scrape-url", is added + (1) tr_stat's "tracker" field is omitted. + (2) a new string, "announce-url", is added. + (3) a new string, "scrape-url", is added. 3.4. Adding a Torrent @@ -122,15 +122,15 @@ string | value type & description -------------------+------------------------------------------------- - "paused" | boolean if true, don't start the torrent "destination" | string path to download the torrent to "filename" | string location of the .torrent file + "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. Response arguments: on success, a "torrent-added" object in the - form of one of 3.1's tr_info objects. + form of one of 3.2's tr_info objects. 3.5. Other torrent settings @@ -172,16 +172,16 @@ 3.6.1. Mutators - Method name: "torrent-set-file" + Method name: "torrent-set-priorities" Request arguments: 3.1's "ids", plus one or more of 3.6's arguments Response arguments: none 3.6.2. Accessors - Method name: "torrent-get-file" + Method name: "torrent-get-priorities" Request arguments: none - Response arguments: A "torrents" list of objects containing all - of 3.6's arguments plus the torrent's "id" int. + Response arguments: A "torrents" list of objects containing all + of 3.6's arguments plus the torrent's "id" int. 4. Session Status Requests @@ -191,9 +191,9 @@ ---------------------------+------------------------------------------------- "encryption" | string "required", "preferred", "tolerated" "peer-limit" | int maximum global number of peers + "pex-allowed" | boolean true means allow pex in public torrents "port" | int port number "port-forwarding-enabled" | boolean true means enabled. - "pex-allowed" | boolean true means allow pex in public torrents "speed-limit-down" | int max global download speed (in KiB/s) "speed-limit-down-enabled" | int max global download speed (in KiB/s) "speed-limit-up" | int max global upload speed (in KiB/s) diff --git a/libtransmission/bencode-test.c b/libtransmission/bencode-test.c index a4dbb417a..d1c80ce2f 100644 --- a/libtransmission/bencode-test.c +++ b/libtransmission/bencode-test.c @@ -275,10 +275,8 @@ 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; @@ -296,6 +294,11 @@ testRISON( void ) if(( val = testRISONSnippet( rison, expected ))) return val; + rison = "(method:torrent-info)"; + expected = "{ \"method\": \"torrent-info\" }"; + if(( val = testRISONSnippet( rison, expected ))) + return val; + return 0; } diff --git a/libtransmission/json.c b/libtransmission/json.c index aab24964a..59cd50f8a 100644 --- a/libtransmission/json.c +++ b/libtransmission/json.c @@ -204,6 +204,7 @@ tr_rison2json( const char * str, int rison_len ) 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; diff --git a/libtransmission/rpc.c b/libtransmission/rpc.c index 50064b34e..e12c0fd9d 100644 --- a/libtransmission/rpc.c +++ b/libtransmission/rpc.c @@ -69,7 +69,8 @@ getTorrents( tr_handle * handle, tr_benc * args, int * setmeCount ) typedef void( *tor_func )( tr_torrent * tor ); -static void callTorrentFunc( tr_handle * h, tr_benc * args_in, tor_func func ) +static void +callTorrentFunc( tr_handle * h, tr_benc * args_in, tor_func func ) { int i, torrentCount; tr_torrent ** torrents = getTorrents( h, args_in, &torrentCount ); @@ -164,7 +165,8 @@ torrentStatus( tr_handle * handle, tr_benc * args_in, tr_benc * args_out ) tr_bencDictAddInt( t, "nextScrapeTime", s->nextScrapeTime ); tr_bencDictAddInt( t, "lastAnnounceTime", s->lastAnnounceTime ); tr_bencDictAddInt( t, "nextAnnounceTime", s->nextAnnounceTime ); - tr_bencDictAddInt( t, "nextManualAnnounceTime", s->nextManualAnnounceTime ); + tr_bencDictAddInt( t, "nextManualAnnounceTime", + s->nextManualAnnounceTime ); } /* cleanup */ @@ -172,6 +174,10 @@ torrentStatus( tr_handle * handle, tr_benc * args_in, tr_benc * args_out ) return NULL; } +/** +*** +**/ + static void addFiles( const tr_info * info, tr_benc * files ) { @@ -202,24 +208,24 @@ addTrackers( const tr_info * info, tr_benc * trackers ) } static void -serializeInfo( const tr_torrent * tor, tr_benc * d ) +addInfo( const tr_torrent * tor, tr_benc * d ) { const tr_info * inf = tr_torrentInfo( tor ); tr_bencInitDict( d, 14 ); - tr_bencDictAddInt( d, "id", tor->uniqueId ); - tr_bencDictAddStr( d, "torrent", inf->torrent ); - tr_bencDictAddStr( d, "hashString", inf->hashString ); - tr_bencDictAddStr( d, "name", inf->name ); tr_bencDictAddStr( d, "comment", inf->comment ? inf->comment : "" ); tr_bencDictAddStr( d, "creator", inf->creator ? inf->creator : "" ); - tr_bencDictAddInt( d, "isPrivate", inf->isPrivate ); - tr_bencDictAddInt( d, "isMultifile", inf->isMultifile ); tr_bencDictAddInt( d, "dateCreated", inf->dateCreated ); - tr_bencDictAddInt( d, "pieceSize", inf->pieceSize ); + tr_bencDictAddStr( d, "hashString", inf->hashString ); + tr_bencDictAddInt( d, "id", tor->uniqueId ); + tr_bencDictAddInt( d, "isMultifile", inf->isMultifile ); + tr_bencDictAddInt( d, "isPrivate", inf->isPrivate ); + tr_bencDictAddStr( d, "name", inf->name ); tr_bencDictAddInt( d, "pieceCount", inf->pieceCount ); + tr_bencDictAddInt( d, "pieceSize", inf->pieceSize ); + tr_bencDictAddStr( d, "torrent", inf->torrent ); tr_bencDictAddInt( d, "totalSize", inf->totalSize ); addFiles ( inf, tr_bencDictAddList( d, "files", inf->fileCount ) ); - addTrackers( inf, tr_bencDictAddList( d, "files", inf->trackerCount ) ); + addTrackers( inf, tr_bencDictAddList( d, "trackers", inf->trackerCount ) ); } static const char* @@ -227,15 +233,19 @@ torrentInfo( tr_handle * handle, tr_benc * args_in, tr_benc * args_out ) { int i, torrentCount; tr_torrent ** torrents = getTorrents( handle, args_in, &torrentCount ); - tr_benc * list = tr_bencDictAddList( args_out, "status", torrentCount ); + tr_benc * list = tr_bencDictAddList( args_out, "info", torrentCount ); for( i=0; iuniqueId ); - buildFileList( tor, d, "priority-high", testFileHigh ); - buildFileList( tor, d, "priority-low", testFileLow ); - buildFileList( tor, d, "priority-normal", testFileNormal ); buildFileList( tor, d, "download", testFileDownload ); buildFileList( tor, d, "no-download", testFileDND ); + buildFileList( tor, d, "priority-low", testFileLow ); + buildFileList( tor, d, "priority-normal", testFileNormal ); + buildFileList( tor, d, "priority-high", testFileHigh ); } tr_free( torrents ); @@ -370,10 +380,10 @@ torrentGetFile( tr_handle * handle, tr_benc * args_in, tr_benc * args_out ) static void setFilePriorities( tr_torrent * tor, int priority, tr_benc * list ) { - const int n = tr_bencListSize( list ); int i; int64_t tmp; int fileCount = 0; + const int n = tr_bencListSize( list ); tr_file_index_t * files = tr_new0( tr_file_index_t, n ); for( i=0; i %s <--\n", request ); + /* possibly convert o-rison to rison */ + if( request && ( *request != '(' ) ) + { + const int n = strlen( request ); + char * tmp = tr_new( char, n + 3 ); + tmp[0] = '('; + memcpy( tmp+1, request, n ); + tmp[n+1] = ')'; + tmp[n+2] = '\0'; + tr_free( request ); + request = tmp; + } +fprintf( stderr, "after o-rison conversion: --> %s <--\n", request ); + + /* convert rison to json */ + { + char * tmp = tr_rison2json( request, strlen( request ) ); + tr_free( request ); + request = tmp; + } +fprintf( stderr, "after json conversion: --> %s <--\n", request ); + + /* parse the json */ + have_content = !tr_jsonParse( request, request+strlen(request), &top, NULL ); + if( have_content ) + { + /* for convenience' sake, our URI rpc notation allows the + * `args' object to be declared implicitly... */ + tr_benc tmp, *args; + int64_t i; + const char * str; + tr_bencInitDict( &tmp, 3 ); + if( tr_bencDictFindInt( &top, "tag", &i ) ) + tr_bencDictAddInt( &tmp, "tag", i ); + if( tr_bencDictFindStr( &top, "method", &str ) ) + tr_bencDictAddStr( &tmp, "method", str ); + args = tr_bencDictAdd( &tmp, "args" ); + *args = top; +tr_bencPrint( &tmp); + ret = request_exec( handle, &tmp, response_len ); + tr_bencInitInt( args, 0 ); + tr_bencFree( &tmp ); + tr_bencFree( &top ); + } + + tr_free( request ); return ret; }