2. Message Format
- Messages are formatted in a subset of JSON easily represented
- as bencoded data. Arrays, objects, strings, and whole numbers
- all have one-to-one mappings between JSON and benc.
-
- Booleans and floating-point numbers are also used in the JSON messages.
- Those two types aren't native to benc, so they're encoded this way:
- Booleans are encoded as numbers where 0 is false and 1 is true.
- Floating-point numbers are represented as strings.
-
Messages are formatted as objects. There are two types:
requests (described in 2.1) and responses (described in 2.2).
string | value type & description
----------------------------------+-------------------------------------------------
"downloadLimit" | number maximum download speed (in K/s)
- "downloadLimited" | 'boolean' true if "downloadLimit" is honored
+ "downloadLimited" | boolean true if "downloadLimit" is honored
"files-wanted" | array indices of file(s) to download
"files-unwanted" | array indices of file(s) to not download
- "honorsSessionLimits" | 'boolean' true if session upload limits are honored
+ "honorsSessionLimits" | boolean true if session upload limits are honored
"ids" | array torrent list, as described in 3.1
"peer-limit" | number maximum number of peers
"priority-high" | array indices of high-priority file(s)
"priority-low" | array indices of low-priority file(s)
"priority-normal" | array indices of normal-priority file(s)
- "seedRatioLimit" | 'double' session seeding ratio
+ "seedRatioLimit" | double session seeding ratio
"seedRatioMode" | number which ratio to use. See tr_ratiolimit
"uploadLimit" | number maximum upload speed (in K/s)
- "uploadLimited" | 'boolean' true if "uploadLimit" is honored
+ "uploadLimited" | boolean true if "uploadLimit" is honored
Just as an empty "ids" value is shorthand for "all ids", using an empty array
for "files-wanted", "files-unwanted", "priority-high", "priority-low", or
downloadedEver | number | tr_stat
downloaders | number | tr_stat
downloadLimit | number | tr_torrent
- downloadLimited | 'boolean' | tr_torrent
+ downloadLimited | boolean | tr_torrent
error | number | tr_stat
errorString | number | tr_stat
eta | number | tr_stat
hashString | string | tr_info
haveUnchecked | number | tr_stat
haveValid | number | tr_stat
- honorsSessionLimits | 'boolean' | tr_torrent
+ honorsSessionLimits | boolean | tr_torrent
id | number | tr_torrent
- isPrivate | 'boolean' | tr_torrent
+ isPrivate | boolean | tr_torrent
lastAnnounceTime | number | tr_stat
lastScrapeTime | number | tr_stat
leechers | number | tr_stat
peersGettingFromUs | number | tr_stat
peersKnown | number | tr_stat
peersSendingToUs | number | tr_stat
- percentDone | 'double' | tr_stat
+ percentDone | double | tr_stat
pieces | string (see below) | tr_torrent
pieceCount | tnumber | tr_info
pieceSize | tnumber | tr_info
priorities | array (see below) | n/a
- ratio | 'double' | tr_stat
+ ratio | double | tr_stat
rateDownload (B/s) | number | tr_stat
rateUpload (B/s) | number | tr_stat
- recheckProgress | 'double' | tr_stat
+ recheckProgress | double | tr_stat
scrapeResponse | string | tr_stat
scrapeURL | string | tr_stat
seeders | number | tr_stat
- seedRatioLimit | 'double' | tr_torrent
+ seedRatioLimit | double | tr_torrent
seedRatioMode | number | tr_ratiolimit
sizeWhenDone | number | tr_stat
startDate | number | tr_stat
torrentFile | string | tr_info
uploadedEver | number | tr_stat
uploadLimit | number | tr_torrent
- uploadLimited | 'boolean' | tr_torrent
- uploadRatio | 'double' | tr_stat
+ uploadLimited | boolean | tr_torrent
+ uploadRatio | double | tr_stat
wanted | array (see below) | n/a
webseeds | array (see below) | n/a
webseedsSendingToUs | number | tr_stat
| each containing: |
+-------------------------+------------+
| bytesCompleted | number | tr_torrent
- | wanted | 'boolean' | tr_info
+ | wanted | boolean | tr_info
| priority | number | tr_info
-----------------------+--------------------------------------+
peers | array of objects, each containing: |
+-------------------------+------------+
| address | string | tr_peer_stat
| clientName | string | tr_peer_stat
- | clientIsChoked | 'boolean' | tr_peer_stat
- | clientIsInterested | 'boolean' | tr_peer_stat
- | isDownloadingFrom | 'boolean' | tr_peer_stat
- | isEncrypted | 'boolean' | tr_peer_stat
- | isIncoming | 'boolean' | tr_peer_stat
- | isUploadingTo | 'boolean' | tr_peer_stat
- | peerIsChoked | 'boolean' | tr_peer_stat
- | peerIsInterested | 'boolean' | tr_peer_stat
+ | clientIsChoked | boolean | tr_peer_stat
+ | clientIsInterested | boolean | tr_peer_stat
+ | isDownloadingFrom | boolean | tr_peer_stat
+ | isEncrypted | boolean | tr_peer_stat
+ | isIncoming | boolean | tr_peer_stat
+ | isUploadingTo | boolean | tr_peer_stat
+ | peerIsChoked | boolean | tr_peer_stat
+ | peerIsInterested | boolean | tr_peer_stat
| port | number | tr_peer_stat
- | progress | 'double' | tr_peer_stat
+ | progress | double | tr_peer_stat
| rateToClient (B/s) | number | tr_peer_stat
| rateToPeer (B/s) | number | tr_peer_stat
-----------------------+--------------------------------------+
"download-dir" | string path to download the torrent to
"filename" | string filename or URL of the .torrent file
"metainfo" | string base64-encoded .torrent content
- "paused" | 'boolean' if true, don't start the torrent
+ "paused" | boolean if true, don't start the torrent
"peer-limit" | number maximum number of peers
"files-wanted" | array indices of file(s) to download
"files-unwanted" | array indices of file(s) to not download
string | value type & description
---------------------------+-------------------------------------------------
"ids" | array torrent list, as described in 3.1
- "delete-local-data" | 'boolean' delete local data. (default: false)
+ "delete-local-data" | boolean delete local data. (default: false)
Response arguments: none
string | value type & description
---------------------------+-------------------------------------------------
"alt-speed-down" | number max global download speed (in K/s)
- "alt-speed-enabled" | 'boolean' true means use the alt speeds
+ "alt-speed-enabled" | boolean true means use the alt speeds
"alt-speed-time-begin" | number when to turn on alt speeds (units: minutes after midnight)
- "alt-speed-time-enabled" | 'boolean' true means the scheduled on/off times are used
+ "alt-speed-time-enabled" | boolean true means the scheduled on/off times are used
"alt-speed-time-end" | number when to turn off alt speeds (units: same)
"alt-speed-time-day" | number what day(s) to turn on alt speeds (look at tr_sched_day)
"alt-speed-up" | number max global upload speed (in K/s)
- "blocklist-enabled" | 'boolean' true means enabled
+ "blocklist-enabled" | boolean true means enabled
"blocklist-size" | number number of rules in the blocklist
"encryption" | string "required", "preferred", "tolerated"
"download-dir" | string default path to download torrents
"peer-limit-global" | number maximum global number of peers
"peer-limit-per-torrent" | number maximum global number of peers
- "pex-enabled" | 'boolean' true means allow pex in public torrents
+ "pex-enabled" | boolean true means allow pex in public torrents
"peer-port" | number port number
- "peer-port-random-on-start"| 'boolean' true means pick a random peer port on launch
- "port-forwarding-enabled" | 'boolean' true means enabled
+ "peer-port-random-on-start"| boolean true means pick a random peer port on launch
+ "port-forwarding-enabled" | boolean true means enabled
"rpc-version" | number the current RPC API version
"rpc-version-minimum" | number the minimum RPC API version supported
- "seedRatioLimit" | 'double' the default seed ratio for torrents to use
- "seedRatioLimited" | 'boolean' true if seedRatioLimit is honored by default
+ "seedRatioLimit" | double the default seed ratio for torrents to use
+ "seedRatioLimited" | boolean true if seedRatioLimit is honored by default
"speed-limit-down" | number max global download speed (in K/s)
- "speed-limit-down-enabled" | 'boolean' true means enabled
+ "speed-limit-down-enabled" | boolean true means enabled
"speed-limit-up" | number max global upload speed (in K/s)
- "speed-limit-up-enabled" | 'boolean' true means enabled
+ "speed-limit-up-enabled" | boolean true means enabled
"version" | string long version string "$version ($revision)"
"rpc-version" indicates the RPC interface version supported by the RPC server.
tr_bencGetInt( const tr_benc * val,
int64_t * setme )
{
- const tr_bool success = tr_bencIsInt( val );
+ tr_bool success = FALSE;
- if( success && setme )
- *setme = val->val.i;
+ if( !success && (( success = tr_bencIsInt( val ))))
+ if( setme )
+ *setme = val->val.i;
+
+ if( !success && (( success = tr_bencIsBool( val )))) {
+ fprintf( stderr, "warning: reading bool as an int\n" );
+ if( setme )
+ *setme = val->val.b ? 1 : 0;
+ }
return success;
}
{
tr_bool success = FALSE;
+ if(( success = tr_bencIsBool( val )))
+ *setme = val->val.b;
+
if( !success && tr_bencIsInt( val ) )
if(( success = ( val->val.i==0 || val->val.i==1 ) ))
*setme = val->val.i!=0;
{
tr_bool success = FALSE;
+ if( !success && (( success = tr_bencIsReal( val ))))
+ *setme = val->val.d;
+
+ if( !success && (( success = tr_bencIsInt( val ))))
+ *setme = val->val.i;
+
if( !success && tr_bencIsString(val) )
{
char * endptr;
*setme = d;
}
- if( !success && tr_bencIsInt(val) )
- {
- success = TRUE;
- *setme = val->val.i;
- }
return success;
}
}
void
-tr_bencInitInt( tr_benc * val,
- int64_t num )
+tr_bencInitBool( tr_benc * b, int value )
+{
+ tr_bencInit( b, TYPE_BOOL );
+ b->val.b = value != 0;
+}
+
+void
+tr_bencInitReal( tr_benc * b, double value )
{
- tr_bencInit( val, TYPE_INT );
- val->val.i = num;
+ tr_bencInit( b, TYPE_REAL );
+ b->val.d = value;
+}
+
+void
+tr_bencInitInt( tr_benc * b, int64_t value )
+{
+ tr_bencInit( b, TYPE_INT );
+ b->val.i = value;
}
int
-tr_bencInitList( tr_benc * val,
- size_t reserveCount )
+tr_bencInitList( tr_benc * b, size_t reserveCount )
{
- tr_bencInit( val, TYPE_LIST );
- return tr_bencListReserve( val, reserveCount );
+ tr_bencInit( b, TYPE_LIST );
+ return tr_bencListReserve( b, reserveCount );
}
int
-tr_bencListReserve( tr_benc * val,
- size_t count )
+tr_bencListReserve( tr_benc * b, size_t count )
{
- assert( tr_bencIsList( val ) );
- return makeroom( val, count );
+ assert( tr_bencIsList( b ) );
+ return makeroom( b, count );
}
int
-tr_bencInitDict( tr_benc * val,
- size_t reserveCount )
+tr_bencInitDict( tr_benc * b, size_t reserveCount )
{
- tr_bencInit( val, TYPE_DICT );
- return tr_bencDictReserve( val, reserveCount );
+ tr_bencInit( b, TYPE_DICT );
+ return tr_bencDictReserve( b, reserveCount );
}
int
-tr_bencDictReserve( tr_benc * val,
- size_t reserveCount )
+tr_bencDictReserve( tr_benc * b, size_t reserveCount )
{
- assert( tr_bencIsDict( val ) );
- return makeroom( val, reserveCount * 2 );
+ assert( tr_bencIsDict( b ) );
+ return makeroom( b, reserveCount * 2 );
}
tr_benc *
return itemval;
}
-tr_benc*
-tr_bencDictAddInt( tr_benc * dict,
- const char * key,
- int64_t val )
+static tr_benc*
+dictFindOrAdd( tr_benc * dict, const char * key, int type )
{
tr_benc * child;
/* see if it already exists, and if so, try to reuse it */
if(( child = tr_bencDictFind( dict, key ))) {
- if( !tr_bencIsInt( child ) ) {
+ if( !tr_bencIsType( child, type ) ) {
tr_bencDictRemove( dict, key );
child = NULL;
}
if( child == NULL )
child = tr_bencDictAdd( dict, key );
- /* set it */
- tr_bencInitInt( child, val );
+ return child;
+}
+tr_benc*
+tr_bencDictAddInt( tr_benc * dict,
+ const char * key,
+ int64_t val )
+{
+ tr_benc * child = dictFindOrAdd( dict, key, TYPE_INT );
+ tr_bencInitInt( child, val );
return child;
}
tr_benc*
tr_bencDictAddBool( tr_benc * dict, const char * key, tr_bool val )
{
- assert( tr_isBool( val ) );
+ tr_benc * child = dictFindOrAdd( dict, key, TYPE_BOOL );
+ tr_bencInitBool( child, val );
+ return child;
+}
- return tr_bencDictAddInt( dict, key, val );
+tr_benc*
+tr_bencDictAddReal( tr_benc * dict, const char * key, double val )
+{
+ tr_benc * child = dictFindOrAdd( dict, key, TYPE_REAL );
+ tr_bencInitReal( child, val );
+ return child;
}
tr_benc*
return child;
}
+#if 0
tr_benc*
tr_bencDictAddReal( tr_benc * dict, const char * key, double d )
{
+ ccc
char buf[128];
char * locale;
return tr_bencDictAddStr( dict, key, buf );
}
+#endif
tr_benc*
tr_bencDictAddList( tr_benc * dict,
struct WalkFuncs
{
BencWalkFunc intFunc;
+ BencWalkFunc boolFunc;
+ BencWalkFunc realFunc;
BencWalkFunc stringFunc;
BencWalkFunc dictBeginFunc;
BencWalkFunc listBeginFunc;
walkFuncs->intFunc( val, user_data );
break;
+ case TYPE_BOOL:
+ walkFuncs->boolFunc( val, user_data );
+ break;
+
+ case TYPE_REAL:
+ walkFuncs->realFunc( val, user_data );
+ break;
+
case TYPE_STR:
walkFuncs->stringFunc( val, user_data );
break;
evbuffer_add_printf( evbuf, "i%" PRId64 "e", val->val.i );
}
+static void
+saveBoolFunc( const tr_benc * val, void * evbuf )
+{
+ evbuffer_add_printf( evbuf, "i%de", val->val.b?1:0 );
+}
+
+static void
+saveRealFunc( const tr_benc * val, void * evbuf )
+{
+ char buf[128];
+ char * locale;
+ size_t len;
+
+ /* always use a '.' decimal point s.t. locale-hopping doesn't bite us */
+ locale = tr_strdup( setlocale ( LC_NUMERIC, NULL ) );
+ setlocale( LC_NUMERIC, "POSIX" );
+ tr_snprintf( buf, sizeof( buf ), "%f", val->val.d );
+ setlocale( LC_NUMERIC, locale );
+ tr_free( locale );
+
+ len = strlen( buf );
+ evbuffer_add_printf( evbuf, "%lu:", (unsigned long)buf );
+ evbuffer_add( evbuf, buf, len );
+}
+
static void
saveStringFunc( const tr_benc * val,
void * vevbuf )
struct evbuffer * out = tr_getBuffer( );
walkFuncs.intFunc = saveIntFunc;
+ walkFuncs.boolFunc = saveBoolFunc;
+ walkFuncs.realFunc = saveRealFunc;
walkFuncs.stringFunc = saveStringFunc;
walkFuncs.dictBeginFunc = saveDictBeginFunc;
walkFuncs.listBeginFunc = saveListBeginFunc;
struct WalkFuncs walkFuncs;
walkFuncs.intFunc = freeDummyFunc;
+ walkFuncs.boolFunc = freeDummyFunc;
+ walkFuncs.realFunc = freeDummyFunc;
walkFuncs.stringFunc = freeStringFunc;
walkFuncs.dictBeginFunc = freeContainerBeginFunc;
walkFuncs.listBeginFunc = freeContainerBeginFunc;
jsonChildFunc( data );
}
+static void
+jsonBoolFunc( const tr_benc * val, void * vdata )
+{
+ struct jsonWalk * data = vdata;
+
+ evbuffer_add_printf( data->out, "%s", (val->val.b?"true":"false") );
+ jsonChildFunc( data );
+}
+
+static void
+jsonRealFunc( const tr_benc * val, void * vdata )
+{
+ struct jsonWalk * data = vdata;
+ char * locale;
+
+ /* json requires a '.' decimal point regardless of locale */
+ locale = tr_strdup( setlocale ( LC_NUMERIC, NULL ) );
+ setlocale( LC_NUMERIC, "POSIX" );
+ evbuffer_add_printf( data->out, "%f", val->val.d );
+ setlocale( LC_NUMERIC, locale );
+ tr_free( locale );
+
+ jsonChildFunc( data );
+}
+
static void
jsonStringFunc( const tr_benc * val,
void * vdata )
data.parents = NULL;
walkFuncs.intFunc = jsonIntFunc;
+ walkFuncs.boolFunc = jsonBoolFunc;
+ walkFuncs.realFunc = jsonRealFunc;
walkFuncs.stringFunc = jsonStringFunc;
walkFuncs.dictBeginFunc = jsonDictBeginFunc;
walkFuncs.listBeginFunc = jsonListBeginFunc;