1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2024-12-26 09:37:56 +00:00
transmission/libtransmission/rpcimpl.c
Jordan Lee df80c1ce03 Fix the size for the peers and peersFrom dictionaries.
By setting the initial allocation size correctly, we can avoid unnecessary resizing when adding all the dictionary's fields.
2012-07-01 01:42:58 +00:00

1985 lines
71 KiB
C

/*
* This file Copyright (C) Mnemosyne LLC
*
* 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$
*/
#include <assert.h>
#include <ctype.h> /* isdigit */
#include <errno.h>
#include <stdlib.h> /* strtol */
#include <string.h> /* strcmp */
#include <unistd.h> /* unlink */
#ifdef HAVE_ZLIB
#include <zlib.h>
#endif
#include <event2/buffer.h>
#include "transmission.h"
#include "bencode.h"
#include "completion.h"
#include "fdlimit.h"
#include "json.h"
#include "rpcimpl.h"
#include "session.h"
#include "torrent.h"
#include "utils.h"
#include "version.h"
#include "web.h"
#define RPC_VERSION 14
#define RPC_VERSION_MIN 1
#define RECENTLY_ACTIVE_SECONDS 60
#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) )
#if 0
#define dbgmsg(fmt, ...) \
do { \
fprintf( stderr, "%s:%d"#fmt, __FILE__, __LINE__, __VA_ARGS__ ); \
fprintf( stderr, "\n" ); \
} while( 0 )
#else
#define dbgmsg( ... ) \
do { \
if( tr_deepLoggingIsActive( ) ) \
tr_deepLog( __FILE__, __LINE__, "RPC", __VA_ARGS__ ); \
} while( 0 )
#endif
/***
****
***/
static tr_rpc_callback_status
notify( tr_session * session,
int type,
tr_torrent * tor )
{
tr_rpc_callback_status status = 0;
if( session->rpc_func )
status = session->rpc_func( session, type, tor,
session->rpc_func_user_data );
return status;
}
/***
****
***/
/* For functions that can't be immediately executed, like torrentAdd,
* this is the callback data used to pass a response to the caller
* when the task is complete */
struct tr_rpc_idle_data
{
tr_session * session;
tr_benc * response;
tr_benc * args_out;
tr_rpc_response_func callback;
void * callback_user_data;
};
static void
tr_idle_function_done( struct tr_rpc_idle_data * data, const char * result )
{
struct evbuffer * buf;
if( result == NULL )
result = "success";
tr_bencDictAddStr( data->response, "result", result );
buf = tr_bencToBuf( data->response, TR_FMT_JSON_LEAN );
(*data->callback)( data->session, buf, data->callback_user_data );
evbuffer_free( buf );
tr_bencFree( data->response );
tr_free( data->response );
tr_free( data );
}
/***
****
***/
static tr_torrent **
getTorrents( tr_session * session,
tr_benc * args,
int * setmeCount )
{
int torrentCount = 0;
int64_t id;
tr_torrent ** torrents = NULL;
tr_benc * ids;
const char * str;
if( tr_bencDictFindList( args, "ids", &ids ) )
{
int i;
const int n = tr_bencListSize( ids );
torrents = tr_new0( tr_torrent *, n );
for( i = 0; i < n; ++i )
{
tr_torrent * tor = NULL;
tr_benc * node = tr_bencListChild( ids, i );
const char * str;
if( tr_bencGetInt( node, &id ) )
tor = tr_torrentFindFromId( session, id );
else if( tr_bencGetStr( node, &str ) )
tor = tr_torrentFindFromHashString( session, str );
if( tor )
torrents[torrentCount++] = tor;
}
}
else if( tr_bencDictFindInt( args, "ids", &id )
|| tr_bencDictFindInt( args, "id", &id ) )
{
tr_torrent * tor;
torrents = tr_new0( tr_torrent *, 1 );
if( ( tor = tr_torrentFindFromId( session, id ) ) )
torrents[torrentCount++] = tor;
}
else if( tr_bencDictFindStr( args, "ids", &str ) )
{
if( !strcmp( str, "recently-active" ) )
{
tr_torrent * tor = NULL;
const time_t now = tr_time( );
const time_t window = RECENTLY_ACTIVE_SECONDS;
const int n = tr_sessionCountTorrents( session );
torrents = tr_new0( tr_torrent *, n );
while( ( tor = tr_torrentNext( session, tor ) ) )
if( tor->anyDate >= now - window )
torrents[torrentCount++] = tor;
}
else
{
tr_torrent * tor;
torrents = tr_new0( tr_torrent *, 1 );
if(( tor = tr_torrentFindFromHashString( session, str )))
torrents[torrentCount++] = tor;
}
}
else /* all of them */
{
tr_torrent * tor = NULL;
const int n = tr_sessionCountTorrents( session );
torrents = tr_new0( tr_torrent *, n );
while( ( tor = tr_torrentNext( session, tor ) ) )
torrents[torrentCount++] = tor;
}
*setmeCount = torrentCount;
return torrents;
}
static void
notifyBatchQueueChange( tr_session * session, tr_torrent ** torrents, int n )
{
int i;
for( i=0; i<n; ++i )
notify( session, TR_RPC_TORRENT_CHANGED, torrents[i] );
notify( session, TR_RPC_SESSION_QUEUE_POSITIONS_CHANGED, NULL );
}
static const char*
queueMoveTop( tr_session * session,
tr_benc * args_in,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data UNUSED )
{
int n;
tr_torrent ** torrents = getTorrents( session, args_in, &n );
tr_torrentsQueueMoveTop( torrents, n );
notifyBatchQueueChange( session, torrents, n );
tr_free( torrents );
return NULL;
}
static const char*
queueMoveUp( tr_session * session,
tr_benc * args_in,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data UNUSED )
{
int n;
tr_torrent ** torrents = getTorrents( session, args_in, &n );
tr_torrentsQueueMoveUp( torrents, n );
notifyBatchQueueChange( session, torrents, n );
tr_free( torrents );
return NULL;
}
static const char*
queueMoveDown( tr_session * session,
tr_benc * args_in,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data UNUSED )
{
int n;
tr_torrent ** torrents = getTorrents( session, args_in, &n );
tr_torrentsQueueMoveDown( torrents, n );
notifyBatchQueueChange( session, torrents, n );
tr_free( torrents );
return NULL;
}
static const char*
queueMoveBottom( tr_session * session,
tr_benc * args_in,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data UNUSED )
{
int n;
tr_torrent ** torrents = getTorrents( session, args_in, &n );
tr_torrentsQueueMoveBottom( torrents, n );
notifyBatchQueueChange( session, torrents, n );
tr_free( torrents );
return NULL;
}
static const char*
torrentStart( tr_session * session,
tr_benc * args_in,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data UNUSED )
{
int i, torrentCount;
tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
assert( idle_data == NULL );
for( i = 0; i < torrentCount; ++i )
{
tr_torrent * tor = torrents[i];
if( !tor->isRunning )
{
tr_torrentStart( tor );
notify( session, TR_RPC_TORRENT_STARTED, tor );
}
}
tr_free( torrents );
return NULL;
}
static const char*
torrentStartNow( tr_session * session,
tr_benc * args_in,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data UNUSED )
{
int i, torrentCount;
tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
assert( idle_data == NULL );
for( i = 0; i < torrentCount; ++i )
{
tr_torrent * tor = torrents[i];
if( !tor->isRunning )
{
tr_torrentStartNow( tor );
notify( session, TR_RPC_TORRENT_STARTED, tor );
}
}
tr_free( torrents );
return NULL;
}
static const char*
torrentStop( tr_session * session,
tr_benc * args_in,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data UNUSED )
{
int i, torrentCount;
tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
assert( idle_data == NULL );
for( i = 0; i < torrentCount; ++i )
{
tr_torrent * tor = torrents[i];
if( tor->isRunning || tr_torrentIsQueued( tor ) )
{
tor->isStopping = true;
notify( session, TR_RPC_TORRENT_STOPPED, tor );
}
}
tr_free( torrents );
return NULL;
}
static const char*
torrentRemove( tr_session * session,
tr_benc * args_in,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data UNUSED )
{
int i;
int torrentCount;
tr_rpc_callback_type type;
bool deleteFlag = false;
tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
assert( idle_data == NULL );
tr_bencDictFindBool( args_in, "delete-local-data", &deleteFlag );
type = deleteFlag ? TR_RPC_TORRENT_TRASHING
: TR_RPC_TORRENT_REMOVING;
for( i=0; i<torrentCount; ++i )
{
tr_torrent * tor = torrents[i];
const tr_rpc_callback_status status = notify( session, type, tor );
if( !( status & TR_RPC_NOREMOVE ) )
tr_torrentRemove( tor, deleteFlag, NULL );
}
tr_free( torrents );
return NULL;
}
static const char*
torrentReannounce( tr_session * session,
tr_benc * args_in,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data UNUSED )
{
int i, torrentCount;
tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
assert( idle_data == NULL );
for( i=0; i<torrentCount; ++i )
{
tr_torrent * tor = torrents[i];
if( tr_torrentCanManualUpdate( tor ) )
{
tr_torrentManualUpdate( tor );
notify( session, TR_RPC_TORRENT_CHANGED, tor );
}
}
tr_free( torrents );
return NULL;
}
static const char*
torrentVerify( tr_session * session,
tr_benc * args_in,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data UNUSED )
{
int i, torrentCount;
tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
assert( idle_data == NULL );
for( i = 0; i < torrentCount; ++i )
{
tr_torrent * tor = torrents[i];
tr_torrentVerify( tor );
notify( session, TR_RPC_TORRENT_CHANGED, tor );
}
tr_free( torrents );
return NULL;
}
/***
****
***/
static void
addFileStats( const tr_torrent * tor, tr_benc * list )
{
tr_file_index_t i;
tr_file_index_t n;
const tr_info * info = tr_torrentInfo( tor );
tr_file_stat * files = tr_torrentFiles( tor, &n );
for( i = 0; i < info->fileCount; ++i )
{
const tr_file * file = &info->files[i];
tr_benc * d = tr_bencListAddDict( list, 3 );
tr_bencDictAddInt( d, "bytesCompleted", files[i].bytesCompleted );
tr_bencDictAddInt( d, "priority", file->priority );
tr_bencDictAddBool( d, "wanted", !file->dnd );
}
tr_torrentFilesFree( files, n );
}
static void
addFiles( const tr_torrent * tor,
tr_benc * list )
{
tr_file_index_t i;
tr_file_index_t n;
const tr_info * info = tr_torrentInfo( tor );
tr_file_stat * files = tr_torrentFiles( tor, &n );
for( i = 0; i < info->fileCount; ++i )
{
const tr_file * file = &info->files[i];
tr_benc * d = tr_bencListAddDict( list, 3 );
tr_bencDictAddInt( d, "bytesCompleted", files[i].bytesCompleted );
tr_bencDictAddInt( d, "length", file->length );
tr_bencDictAddStr( d, "name", file->name );
}
tr_torrentFilesFree( files, n );
}
static void
addWebseeds( const tr_info * info,
tr_benc * webseeds )
{
int i;
for( i = 0; i < info->webseedCount; ++i )
tr_bencListAddStr( webseeds, info->webseeds[i] );
}
static void
addTrackers( const tr_info * info,
tr_benc * trackers )
{
int i;
for( i = 0; i < info->trackerCount; ++i )
{
const tr_tracker_info * t = &info->trackers[i];
tr_benc * d = tr_bencListAddDict( trackers, 4 );
tr_bencDictAddStr( d, "announce", t->announce );
tr_bencDictAddInt( d, "id", t->id );
tr_bencDictAddStr( d, "scrape", t->scrape );
tr_bencDictAddInt( d, "tier", t->tier );
}
}
static void
addTrackerStats( const tr_tracker_stat * st, int n, tr_benc * list )
{
int i;
for( i=0; i<n; ++i )
{
const tr_tracker_stat * s = &st[i];
tr_benc * d = tr_bencListAddDict( list, 26 );
tr_bencDictAddStr ( d, "announce", s->announce );
tr_bencDictAddInt ( d, "announceState", s->announceState );
tr_bencDictAddInt ( d, "downloadCount", s->downloadCount );
tr_bencDictAddBool( d, "hasAnnounced", s->hasAnnounced );
tr_bencDictAddBool( d, "hasScraped", s->hasScraped );
tr_bencDictAddStr ( d, "host", s->host );
tr_bencDictAddInt ( d, "id", s->id );
tr_bencDictAddBool( d, "isBackup", s->isBackup );
tr_bencDictAddInt ( d, "lastAnnouncePeerCount", s->lastAnnouncePeerCount );
tr_bencDictAddStr ( d, "lastAnnounceResult", s->lastAnnounceResult );
tr_bencDictAddInt ( d, "lastAnnounceStartTime", s->lastAnnounceStartTime );
tr_bencDictAddBool( d, "lastAnnounceSucceeded", s->lastAnnounceSucceeded );
tr_bencDictAddInt ( d, "lastAnnounceTime", s->lastAnnounceTime );
tr_bencDictAddBool( d, "lastAnnounceTimedOut", s->lastAnnounceTimedOut );
tr_bencDictAddStr ( d, "lastScrapeResult", s->lastScrapeResult );
tr_bencDictAddInt ( d, "lastScrapeStartTime", s->lastScrapeStartTime );
tr_bencDictAddBool( d, "lastScrapeSucceeded", s->lastScrapeSucceeded );
tr_bencDictAddInt ( d, "lastScrapeTime", s->lastScrapeTime );
tr_bencDictAddInt ( d, "lastScrapeTimedOut", s->lastScrapeTimedOut );
tr_bencDictAddInt ( d, "leecherCount", s->leecherCount );
tr_bencDictAddInt ( d, "nextAnnounceTime", s->nextAnnounceTime );
tr_bencDictAddInt ( d, "nextScrapeTime", s->nextScrapeTime );
tr_bencDictAddStr ( d, "scrape", s->scrape );
tr_bencDictAddInt ( d, "scrapeState", s->scrapeState );
tr_bencDictAddInt ( d, "seederCount", s->seederCount );
tr_bencDictAddInt ( d, "tier", s->tier );
}
}
static void
addPeers( const tr_torrent * tor,
tr_benc * list )
{
int i;
int peerCount;
tr_peer_stat * peers = tr_torrentPeers( tor, &peerCount );
tr_bencInitList( list, peerCount );
for( i = 0; i < peerCount; ++i )
{
tr_benc * d = tr_bencListAddDict( list, 16 );
const tr_peer_stat * peer = peers + i;
tr_bencDictAddStr ( d, "address", peer->addr );
tr_bencDictAddStr ( d, "clientName", peer->client );
tr_bencDictAddBool( d, "clientIsChoked", peer->clientIsChoked );
tr_bencDictAddBool( d, "clientIsInterested", peer->clientIsInterested );
tr_bencDictAddStr ( d, "flagStr", peer->flagStr );
tr_bencDictAddBool( d, "isDownloadingFrom", peer->isDownloadingFrom );
tr_bencDictAddBool( d, "isEncrypted", peer->isEncrypted );
tr_bencDictAddBool( d, "isIncoming", peer->isIncoming );
tr_bencDictAddBool( d, "isUploadingTo", peer->isUploadingTo );
tr_bencDictAddBool( d, "isUTP", peer->isUTP );
tr_bencDictAddBool( d, "peerIsChoked", peer->peerIsChoked );
tr_bencDictAddBool( d, "peerIsInterested", peer->peerIsInterested );
tr_bencDictAddInt ( d, "port", peer->port );
tr_bencDictAddReal( d, "progress", peer->progress );
tr_bencDictAddInt ( d, "rateToClient", toSpeedBytes( peer->rateToClient_KBps ) );
tr_bencDictAddInt ( d, "rateToPeer", toSpeedBytes( peer->rateToPeer_KBps ) );
}
tr_torrentPeersFree( peers, peerCount );
}
/* faster-than-strcmp() optimization. This is kind of clumsy,
but addField() was in the profiler's top 10 list, and this
makes it 4x faster... */
#define tr_streq(a,alen,b) ((alen+1==sizeof(b)) && !memcmp(a,b,alen))
static void
addField( const tr_torrent * const tor,
const tr_info * const inf,
const tr_stat * const st,
tr_benc * const d,
const char * const key )
{
const size_t keylen = strlen( key );
if( tr_streq( key, keylen, "activityDate" ) )
tr_bencDictAddInt( d, key, st->activityDate );
else if( tr_streq( key, keylen, "addedDate" ) )
tr_bencDictAddInt( d, key, st->addedDate );
else if( tr_streq( key, keylen, "bandwidthPriority" ) )
tr_bencDictAddInt( d, key, tr_torrentGetPriority( tor ) );
else if( tr_streq( key, keylen, "comment" ) )
tr_bencDictAddStr( d, key, inf->comment ? inf->comment : "" );
else if( tr_streq( key, keylen, "corruptEver" ) )
tr_bencDictAddInt( d, key, st->corruptEver );
else if( tr_streq( key, keylen, "creator" ) )
tr_bencDictAddStr( d, key, inf->creator ? inf->creator : "" );
else if( tr_streq( key, keylen, "dateCreated" ) )
tr_bencDictAddInt( d, key, inf->dateCreated );
else if( tr_streq( key, keylen, "desiredAvailable" ) )
tr_bencDictAddInt( d, key, st->desiredAvailable );
else if( tr_streq( key, keylen, "doneDate" ) )
tr_bencDictAddInt( d, key, st->doneDate );
else if( tr_streq( key, keylen, "downloadDir" ) )
tr_bencDictAddStr( d, key, tr_torrentGetDownloadDir( tor ) );
else if( tr_streq( key, keylen, "downloadedEver" ) )
tr_bencDictAddInt( d, key, st->downloadedEver );
else if( tr_streq( key, keylen, "downloadLimit" ) )
tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit_KBps( tor, TR_DOWN ) );
else if( tr_streq( key, keylen, "downloadLimited" ) )
tr_bencDictAddBool( d, key, tr_torrentUsesSpeedLimit( tor, TR_DOWN ) );
else if( tr_streq( key, keylen, "error" ) )
tr_bencDictAddInt( d, key, st->error );
else if( tr_streq( key, keylen, "errorString" ) )
tr_bencDictAddStr( d, key, st->errorString );
else if( tr_streq( key, keylen, "eta" ) )
tr_bencDictAddInt( d, key, st->eta );
else if( tr_streq( key, keylen, "files" ) )
addFiles( tor, tr_bencDictAddList( d, key, inf->fileCount ) );
else if( tr_streq( key, keylen, "fileStats" ) )
addFileStats( tor, tr_bencDictAddList( d, key, inf->fileCount ) );
else if( tr_streq( key, keylen, "hashString" ) )
tr_bencDictAddStr( d, key, tor->info.hashString );
else if( tr_streq( key, keylen, "haveUnchecked" ) )
tr_bencDictAddInt( d, key, st->haveUnchecked );
else if( tr_streq( key, keylen, "haveValid" ) )
tr_bencDictAddInt( d, key, st->haveValid );
else if( tr_streq( key, keylen, "honorsSessionLimits" ) )
tr_bencDictAddBool( d, key, tr_torrentUsesSessionLimits( tor ) );
else if( tr_streq( key, keylen, "id" ) )
tr_bencDictAddInt( d, key, st->id );
else if( tr_streq( key, keylen, "isFinished" ) )
tr_bencDictAddBool( d, key, st->finished );
else if( tr_streq( key, keylen, "isPrivate" ) )
tr_bencDictAddBool( d, key, tr_torrentIsPrivate( tor ) );
else if( tr_streq( key, keylen, "isStalled" ) )
tr_bencDictAddBool( d, key, st->isStalled );
else if( tr_streq( key, keylen, "leftUntilDone" ) )
tr_bencDictAddInt( d, key, st->leftUntilDone );
else if( tr_streq( key, keylen, "manualAnnounceTime" ) )
tr_bencDictAddInt( d, key, st->manualAnnounceTime );
else if( tr_streq( key, keylen, "maxConnectedPeers" ) )
tr_bencDictAddInt( d, key, tr_torrentGetPeerLimit( tor ) );
else if( tr_streq( key, keylen, "magnetLink" ) ) {
char * str = tr_torrentGetMagnetLink( tor );
tr_bencDictAddStr( d, key, str );
tr_free( str );
}
else if( tr_streq( key, keylen, "metadataPercentComplete" ) )
tr_bencDictAddReal( d, key, st->metadataPercentComplete );
else if( tr_streq( key, keylen, "name" ) )
tr_bencDictAddStr( d, key, tr_torrentName( tor ) );
else if( tr_streq( key, keylen, "percentDone" ) )
tr_bencDictAddReal( d, key, st->percentDone );
else if( tr_streq( key, keylen, "peer-limit" ) )
tr_bencDictAddInt( d, key, tr_torrentGetPeerLimit( tor ) );
else if( tr_streq( key, keylen, "peers" ) )
addPeers( tor, tr_bencDictAdd( d, key ) );
else if( tr_streq( key, keylen, "peersConnected" ) )
tr_bencDictAddInt( d, key, st->peersConnected );
else if( tr_streq( key, keylen, "peersFrom" ) )
{
tr_benc * tmp = tr_bencDictAddDict( d, key, 7 );
const int * f = st->peersFrom;
tr_bencDictAddInt( tmp, "fromCache", f[TR_PEER_FROM_RESUME] );
tr_bencDictAddInt( tmp, "fromDht", f[TR_PEER_FROM_DHT] );
tr_bencDictAddInt( tmp, "fromIncoming", f[TR_PEER_FROM_INCOMING] );
tr_bencDictAddInt( tmp, "fromLpd", f[TR_PEER_FROM_LPD] );
tr_bencDictAddInt( tmp, "fromLtep", f[TR_PEER_FROM_LTEP] );
tr_bencDictAddInt( tmp, "fromPex", f[TR_PEER_FROM_PEX] );
tr_bencDictAddInt( tmp, "fromTracker", f[TR_PEER_FROM_TRACKER] );
}
else if( tr_streq( key, keylen, "peersGettingFromUs" ) )
tr_bencDictAddInt( d, key, st->peersGettingFromUs );
else if( tr_streq( key, keylen, "peersSendingToUs" ) )
tr_bencDictAddInt( d, key, st->peersSendingToUs );
else if( tr_streq( key, keylen, "pieces" ) ) {
size_t byte_count = 0;
void * bytes = tr_cpCreatePieceBitfield( &tor->completion, &byte_count );
char * str = tr_base64_encode( bytes, byte_count, NULL );
tr_bencDictAddStr( d, key, str!=NULL ? str : "" );
tr_free( str );
tr_free( bytes );
}
else if( tr_streq( key, keylen, "pieceCount" ) )
tr_bencDictAddInt( d, key, inf->pieceCount );
else if( tr_streq( key, keylen, "pieceSize" ) )
tr_bencDictAddInt( d, key, inf->pieceSize );
else if( tr_streq( key, keylen, "priorities" ) )
{
tr_file_index_t i;
tr_benc * p = tr_bencDictAddList( d, key, inf->fileCount );
for( i = 0; i < inf->fileCount; ++i )
tr_bencListAddInt( p, inf->files[i].priority );
}
else if( tr_streq( key, keylen, "queuePosition" ) )
tr_bencDictAddInt( d, key, st->queuePosition );
else if( tr_streq( key, keylen, "rateDownload" ) )
tr_bencDictAddInt( d, key, toSpeedBytes( st->pieceDownloadSpeed_KBps ) );
else if( tr_streq( key, keylen, "rateUpload" ) )
tr_bencDictAddInt( d, key, toSpeedBytes( st->pieceUploadSpeed_KBps ) );
else if( tr_streq( key, keylen, "recheckProgress" ) )
tr_bencDictAddReal( d, key, st->recheckProgress );
else if( tr_streq( key, keylen, "seedIdleLimit" ) )
tr_bencDictAddInt( d, key, tr_torrentGetIdleLimit( tor ) );
else if( tr_streq( key, keylen, "seedIdleMode" ) )
tr_bencDictAddInt( d, key, tr_torrentGetIdleMode( tor ) );
else if( tr_streq( key, keylen, "seedRatioLimit" ) )
tr_bencDictAddReal( d, key, tr_torrentGetRatioLimit( tor ) );
else if( tr_streq( key, keylen, "seedRatioMode" ) )
tr_bencDictAddInt( d, key, tr_torrentGetRatioMode( tor ) );
else if( tr_streq( key, keylen, "sizeWhenDone" ) )
tr_bencDictAddInt( d, key, st->sizeWhenDone );
else if( tr_streq( key, keylen, "startDate" ) )
tr_bencDictAddInt( d, key, st->startDate );
else if( tr_streq( key, keylen, "status" ) )
tr_bencDictAddInt( d, key, st->activity );
else if( tr_streq( key, keylen, "secondsDownloading" ) )
tr_bencDictAddInt( d, key, st->secondsDownloading );
else if( tr_streq( key, keylen, "secondsSeeding" ) )
tr_bencDictAddInt( d, key, st->secondsSeeding );
else if( tr_streq( key, keylen, "trackers" ) )
addTrackers( inf, tr_bencDictAddList( d, key, inf->trackerCount ) );
else if( tr_streq( key, keylen, "trackerStats" ) ) {
int n;
tr_tracker_stat * s = tr_torrentTrackers( tor, &n );
addTrackerStats( s, n, tr_bencDictAddList( d, key, n ) );
tr_torrentTrackersFree( s, n );
}
else if( tr_streq( key, keylen, "torrentFile" ) )
tr_bencDictAddStr( d, key, inf->torrent );
else if( tr_streq( key, keylen, "totalSize" ) )
tr_bencDictAddInt( d, key, inf->totalSize );
else if( tr_streq( key, keylen, "uploadedEver" ) )
tr_bencDictAddInt( d, key, st->uploadedEver );
else if( tr_streq( key, keylen, "uploadLimit" ) )
tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit_KBps( tor, TR_UP ) );
else if( tr_streq( key, keylen, "uploadLimited" ) )
tr_bencDictAddBool( d, key, tr_torrentUsesSpeedLimit( tor, TR_UP ) );
else if( tr_streq( key, keylen, "uploadRatio" ) )
tr_bencDictAddReal( d, key, st->ratio );
else if( tr_streq( key, keylen, "wanted" ) )
{
tr_file_index_t i;
tr_benc * w = tr_bencDictAddList( d, key, inf->fileCount );
for( i = 0; i < inf->fileCount; ++i )
tr_bencListAddInt( w, inf->files[i].dnd ? 0 : 1 );
}
else if( tr_streq( key, keylen, "webseeds" ) )
addWebseeds( inf, tr_bencDictAddList( d, key, inf->webseedCount ) );
else if( tr_streq( key, keylen, "webseedsSendingToUs" ) )
tr_bencDictAddInt( d, key, st->webseedsSendingToUs );
}
static void
addInfo( const tr_torrent * tor, tr_benc * d, tr_benc * fields )
{
const char * str;
const int n = tr_bencListSize( fields );
tr_bencInitDict( d, n );
if( n > 0 )
{
int i;
const tr_info const * inf = tr_torrentInfo( tor );
const tr_stat const * st = tr_torrentStat( (tr_torrent*)tor );
for( i=0; i<n; ++i )
if( tr_bencGetStr( tr_bencListChild( fields, i ), &str ) )
addField( tor, inf, st, d, str );
}
}
static const char*
torrentGet( tr_session * session,
tr_benc * args_in,
tr_benc * args_out,
struct tr_rpc_idle_data * idle_data UNUSED )
{
int i, torrentCount;
tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
tr_benc * list = tr_bencDictAddList( args_out, "torrents", torrentCount );
tr_benc * fields;
const char * msg = NULL;
const char * strVal;
assert( idle_data == NULL );
if( tr_bencDictFindStr( args_in, "ids", &strVal ) && !strcmp( strVal, "recently-active" ) ) {
int n = 0;
tr_benc * d;
const time_t now = tr_time( );
const int interval = RECENTLY_ACTIVE_SECONDS;
tr_benc * removed_out = tr_bencDictAddList( args_out, "removed", 0 );
while(( d = tr_bencListChild( &session->removedTorrents, n++ ))) {
int64_t intVal;
if( tr_bencDictFindInt( d, "date", &intVal ) && ( intVal >= now - interval ) ) {
tr_bencDictFindInt( d, "id", &intVal );
tr_bencListAddInt( removed_out, intVal );
}
}
}
if( !tr_bencDictFindList( args_in, "fields", &fields ) )
msg = "no fields specified";
else for( i = 0; i < torrentCount; ++i )
addInfo( torrents[i], tr_bencListAdd( list ), fields );
tr_free( torrents );
return msg;
}
/***
****
***/
static const char*
setFilePriorities( tr_torrent * tor,
int priority,
tr_benc * list )
{
int i;
int64_t tmp;
int fileCount = 0;
const int n = tr_bencListSize( list );
const char * errmsg = NULL;
tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
if( n )
{
for( i = 0; i < n; ++i ) {
if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) {
if( 0 <= tmp && tmp < tor->info.fileCount ) {
files[fileCount++] = tmp;
} else {
errmsg = "file index out of range";
}
}
}
}
else /* if empty set, apply to all */
{
tr_file_index_t t;
for( t = 0; t < tor->info.fileCount; ++t )
files[fileCount++] = t;
}
if( fileCount )
tr_torrentSetFilePriorities( tor, files, fileCount, priority );
tr_free( files );
return errmsg;
}
static const char*
setFileDLs( tr_torrent * tor,
int do_download,
tr_benc * list )
{
int i;
int64_t tmp;
int fileCount = 0;
const int n = tr_bencListSize( list );
const char * errmsg = NULL;
tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
if( n ) /* if argument list, process them */
{
for( i = 0; i < n; ++i ) {
if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) {
if( 0 <= tmp && tmp < tor->info.fileCount ) {
files[fileCount++] = tmp;
} else {
errmsg = "file index out of range";
}
}
}
}
else /* if empty set, apply to all */
{
tr_file_index_t t;
for( t = 0; t < tor->info.fileCount; ++t )
files[fileCount++] = t;
}
if( fileCount )
tr_torrentSetFileDLs( tor, files, fileCount, do_download );
tr_free( files );
return errmsg;
}
static bool
findAnnounceUrl( const tr_tracker_info * t, int n, const char * url, int * pos )
{
int i;
bool found = false;
for( i=0; i<n; ++i )
{
if( !strcmp( t[i].announce, url ) )
{
found = true;
if( pos ) *pos = i;
break;
}
}
return found;
}
static int
copyTrackers( tr_tracker_info * tgt, const tr_tracker_info * src, int n )
{
int i;
int maxTier = -1;
for( i=0; i<n; ++i )
{
tgt[i].tier = src[i].tier;
tgt[i].announce = tr_strdup( src[i].announce );
maxTier = MAX( maxTier, src[i].tier );
}
return maxTier;
}
static void
freeTrackers( tr_tracker_info * trackers, int n )
{
int i;
for( i=0; i<n; ++i )
tr_free( trackers[i].announce );
tr_free( trackers );
}
static const char*
addTrackerUrls( tr_torrent * tor, tr_benc * urls )
{
int i;
int n;
int tier;
tr_benc * val;
tr_tracker_info * trackers;
bool changed = false;
const tr_info * inf = tr_torrentInfo( tor );
const char * errmsg = NULL;
/* make a working copy of the existing announce list */
n = inf->trackerCount;
trackers = tr_new0( tr_tracker_info, n + tr_bencListSize( urls ) );
tier = copyTrackers( trackers, inf->trackers, n );
/* and add the new ones */
i = 0;
while(( val = tr_bencListChild( urls, i++ ) ))
{
const char * announce = NULL;
if( tr_bencGetStr( val, &announce )
&& tr_urlIsValidTracker( announce )
&& !findAnnounceUrl( trackers, n, announce, NULL ) )
{
trackers[n].tier = ++tier; /* add a new tier */
trackers[n].announce = tr_strdup( announce );
++n;
changed = true;
}
}
if( !changed )
errmsg = "invalid argument";
else if( !tr_torrentSetAnnounceList( tor, trackers, n ) )
errmsg = "error setting announce list";
freeTrackers( trackers, n );
return errmsg;
}
static const char*
replaceTrackers( tr_torrent * tor, tr_benc * urls )
{
int i;
tr_benc * pair[2];
tr_tracker_info * trackers;
bool changed = false;
const tr_info * inf = tr_torrentInfo( tor );
const int n = inf->trackerCount;
const char * errmsg = NULL;
/* make a working copy of the existing announce list */
trackers = tr_new0( tr_tracker_info, n );
copyTrackers( trackers, inf->trackers, n );
/* make the substitutions... */
i = 0;
while(((pair[0] = tr_bencListChild(urls,i))) &&
((pair[1] = tr_bencListChild(urls,i+1))))
{
int64_t pos;
const char * newval;
if( tr_bencGetInt( pair[0], &pos )
&& tr_bencGetStr( pair[1], &newval )
&& tr_urlIsValidTracker( newval )
&& pos < n
&& pos >= 0 )
{
tr_free( trackers[pos].announce );
trackers[pos].announce = tr_strdup( newval );
changed = true;
}
i += 2;
}
if( !changed )
errmsg = "invalid argument";
else if( !tr_torrentSetAnnounceList( tor, trackers, n ) )
errmsg = "error setting announce list";
freeTrackers( trackers, n );
return errmsg;
}
static const char*
removeTrackers( tr_torrent * tor, tr_benc * ids )
{
int i;
int n;
int t = 0;
int dup = -1;
int * tids;
tr_benc * val;
tr_tracker_info * trackers;
bool changed = false;
const tr_info * inf = tr_torrentInfo( tor );
const char * errmsg = NULL;
/* make a working copy of the existing announce list */
n = inf->trackerCount;
tids = tr_new0( int, n );
trackers = tr_new0( tr_tracker_info, n );
copyTrackers( trackers, inf->trackers, n );
/* remove the ones specified in the urls list */
i = 0;
while(( val = tr_bencListChild( ids, i++ )))
{
int64_t pos;
if( tr_bencGetInt( val, &pos )
&& pos < n
&& pos >= 0 )
tids[t++] = pos;
}
/* sort trackerIds and remove from largest to smallest so there is no need to recacluate array indicies */
qsort( tids, t, sizeof(int), compareInt );
while( t-- )
{
/* check for duplicates */
if( tids[t] == dup )
continue;
tr_removeElementFromArray( trackers, tids[t], sizeof( tr_tracker_info ), n-- );
dup = tids[t];
changed = true;
}
if( !changed )
errmsg = "invalid argument";
else if( !tr_torrentSetAnnounceList( tor, trackers, n ) )
errmsg = "error setting announce list";
freeTrackers( trackers, n );
tr_free( tids );
return errmsg;
}
static const char*
torrentSet( tr_session * session,
tr_benc * args_in,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data UNUSED )
{
const char * errmsg = NULL;
int i, torrentCount;
tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
assert( idle_data == NULL );
for( i = 0; i < torrentCount; ++i )
{
int64_t tmp;
double d;
tr_benc * files;
tr_benc * trackers;
bool boolVal;
tr_torrent * tor = torrents[i];
if( tr_bencDictFindInt( args_in, "bandwidthPriority", &tmp ) )
if( tr_isPriority( tmp ) )
tr_torrentSetPriority( tor, tmp );
if( !errmsg && tr_bencDictFindList( args_in, "files-unwanted", &files ) )
errmsg = setFileDLs( tor, false, files );
if( !errmsg && tr_bencDictFindList( args_in, "files-wanted", &files ) )
errmsg = setFileDLs( tor, true, files );
if( tr_bencDictFindInt( args_in, "peer-limit", &tmp ) )
tr_torrentSetPeerLimit( tor, tmp );
if( !errmsg && tr_bencDictFindList( args_in, "priority-high", &files ) )
errmsg = setFilePriorities( tor, TR_PRI_HIGH, files );
if( !errmsg && tr_bencDictFindList( args_in, "priority-low", &files ) )
errmsg = setFilePriorities( tor, TR_PRI_LOW, files );
if( !errmsg && tr_bencDictFindList( args_in, "priority-normal", &files ) )
errmsg = setFilePriorities( tor, TR_PRI_NORMAL, files );
if( tr_bencDictFindInt( args_in, "downloadLimit", &tmp ) )
tr_torrentSetSpeedLimit_KBps( tor, TR_DOWN, tmp );
if( tr_bencDictFindBool( args_in, "downloadLimited", &boolVal ) )
tr_torrentUseSpeedLimit( tor, TR_DOWN, boolVal );
if( tr_bencDictFindBool( args_in, "honorsSessionLimits", &boolVal ) )
tr_torrentUseSessionLimits( tor, boolVal );
if( tr_bencDictFindInt( args_in, "uploadLimit", &tmp ) )
tr_torrentSetSpeedLimit_KBps( tor, TR_UP, tmp );
if( tr_bencDictFindBool( args_in, "uploadLimited", &boolVal ) )
tr_torrentUseSpeedLimit( tor, TR_UP, boolVal );
if( tr_bencDictFindInt( args_in, "seedIdleLimit", &tmp ) )
tr_torrentSetIdleLimit( tor, tmp );
if( tr_bencDictFindInt( args_in, "seedIdleMode", &tmp ) )
tr_torrentSetIdleMode( tor, tmp );
if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
tr_torrentSetRatioLimit( tor, d );
if( tr_bencDictFindInt( args_in, "seedRatioMode", &tmp ) )
tr_torrentSetRatioMode( tor, tmp );
if( tr_bencDictFindInt( args_in, "queuePosition", &tmp ) )
tr_torrentSetQueuePosition( tor, tmp );
if( !errmsg && tr_bencDictFindList( args_in, "trackerAdd", &trackers ) )
errmsg = addTrackerUrls( tor, trackers );
if( !errmsg && tr_bencDictFindList( args_in, "trackerRemove", &trackers ) )
errmsg = removeTrackers( tor, trackers );
if( !errmsg && tr_bencDictFindList( args_in, "trackerReplace", &trackers ) )
errmsg = replaceTrackers( tor, trackers );
notify( session, TR_RPC_TORRENT_CHANGED, tor );
}
tr_free( torrents );
return errmsg;
}
static const char*
torrentSetLocation( tr_session * session,
tr_benc * args_in,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data UNUSED )
{
const char * errmsg = NULL;
const char * location = NULL;
assert( idle_data == NULL );
if( !tr_bencDictFindStr( args_in, "location", &location ) )
{
errmsg = "no location";
}
else
{
bool move = false;
int i, torrentCount;
tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
tr_bencDictFindBool( args_in, "move", &move );
for( i=0; i<torrentCount; ++i )
{
tr_torrent * tor = torrents[i];
tr_torrentSetLocation( tor, location, move, NULL, NULL );
notify( session, TR_RPC_TORRENT_MOVED, tor );
}
tr_free( torrents );
}
return errmsg;
}
/***
****
***/
static void
portTested( tr_session * session UNUSED,
bool did_connect UNUSED,
bool did_timeout UNUSED,
long response_code,
const void * response,
size_t response_byte_count,
void * user_data )
{
char result[1024];
struct tr_rpc_idle_data * data = user_data;
if( response_code != 200 )
{
tr_snprintf( result, sizeof( result ), "portTested: http error %ld: %s",
response_code, tr_webGetResponseStr( response_code ) );
}
else /* success */
{
const bool isOpen = response_byte_count && *(char*)response == '1';
tr_bencDictAddBool( data->args_out, "port-is-open", isOpen );
tr_snprintf( result, sizeof( result ), "success" );
}
tr_idle_function_done( data, result );
}
static const char*
portTest( tr_session * session,
tr_benc * args_in UNUSED,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data )
{
const int port = tr_sessionGetPeerPort( session );
char * url = tr_strdup_printf( "http://portcheck.transmissionbt.com/%d", port );
tr_webRun( session, url, NULL, NULL, portTested, idle_data );
tr_free( url );
return NULL;
}
/***
****
***/
static void
gotNewBlocklist( tr_session * session,
bool did_connect UNUSED,
bool did_timeout UNUSED,
long response_code,
const void * response,
size_t response_byte_count,
void * user_data )
{
char result[1024];
struct tr_rpc_idle_data * data = user_data;
*result = '\0';
if( response_code != 200 )
{
tr_snprintf( result, sizeof( result ), "gotNewBlocklist: http error %ld: %s",
response_code, tr_webGetResponseStr( response_code ) );
}
else /* successfully fetched the blocklist... */
{
int fd;
int err;
char * filename;
z_stream stream;
const char * configDir = tr_sessionGetConfigDir( session );
const size_t buflen = 1024 * 128; /* 128 KiB buffer */
uint8_t * buf = tr_valloc( buflen );
/* this is an odd Magic Number required by zlib to enable gz support.
See zlib's inflateInit2() documentation for a full description */
const int windowBits = 15 + 32;
stream.zalloc = (alloc_func) Z_NULL;
stream.zfree = (free_func) Z_NULL;
stream.opaque = (voidpf) Z_NULL;
stream.next_in = (void*) response;
stream.avail_in = response_byte_count;
inflateInit2( &stream, windowBits );
filename = tr_buildPath( configDir, "blocklist.tmp", NULL );
fd = tr_open_file_for_writing( filename );
if( fd < 0 )
tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename, tr_strerror( errno ) );
for( ;; )
{
stream.next_out = (void*) buf;
stream.avail_out = buflen;
err = inflate( &stream, Z_NO_FLUSH );
if( stream.avail_out < buflen ) {
const int e = write( fd, buf, buflen - stream.avail_out );
if( e < 0 ) {
tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename, tr_strerror( errno ) );
break;
}
}
if( err != Z_OK ) {
if( ( err != Z_STREAM_END ) && ( err != Z_DATA_ERROR ) )
tr_snprintf( result, sizeof( result ), _( "Error uncompressing blocklist: %s (%d)" ), zError( err ), err );
break;
}
}
inflateEnd( &stream );
if( err == Z_DATA_ERROR ) /* couldn't inflate it... it's probably already uncompressed */
if( write( fd, response, response_byte_count ) < 0 )
tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename, tr_strerror( errno ) );
if( *result )
tr_err( "%s", result );
else {
/* feed it to the session and give the client a response */
const int rule_count = tr_blocklistSetContent( session, filename );
tr_bencDictAddInt( data->args_out, "blocklist-size", rule_count );
tr_snprintf( result, sizeof( result ), "success" );
}
unlink( filename );
tr_free( filename );
tr_free( buf );
}
tr_idle_function_done( data, result );
}
static const char*
blocklistUpdate( tr_session * session,
tr_benc * args_in UNUSED,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data )
{
tr_webRun( session, session->blocklist_url, NULL, NULL, gotNewBlocklist, idle_data );
return NULL;
}
/***
****
***/
static void
addTorrentImpl( struct tr_rpc_idle_data * data, tr_ctor * ctor )
{
int err = 0;
const char * result = NULL;
tr_torrent * tor = tr_torrentNew( ctor, &err );
tr_ctorFree( ctor );
if( tor )
{
tr_benc fields;
tr_bencInitList( &fields, 3 );
tr_bencListAddStr( &fields, "id" );
tr_bencListAddStr( &fields, "name" );
tr_bencListAddStr( &fields, "hashString" );
addInfo( tor, tr_bencDictAdd( data->args_out, "torrent-added" ), &fields );
notify( data->session, TR_RPC_TORRENT_ADDED, tor );
tr_bencFree( &fields );
}
else if( err == TR_PARSE_DUPLICATE )
{
result = "duplicate torrent";
}
else if( err == TR_PARSE_ERR )
{
result = "invalid or corrupt torrent file";
}
tr_idle_function_done( data, result );
}
struct add_torrent_idle_data
{
struct tr_rpc_idle_data * data;
tr_ctor * ctor;
};
static void
gotMetadataFromURL( tr_session * session UNUSED,
bool did_connect UNUSED,
bool did_timeout UNUSED,
long response_code,
const void * response,
size_t response_byte_count,
void * user_data )
{
struct add_torrent_idle_data * data = user_data;
dbgmsg( "torrentAdd: HTTP response code was %ld (%s); response length was %zu bytes",
response_code, tr_webGetResponseStr( response_code ), response_byte_count );
if( response_code==200 || response_code==221 ) /* http or ftp success.. */
{
tr_ctorSetMetainfo( data->ctor, response, response_byte_count );
addTorrentImpl( data->data, data->ctor );
}
else
{
char result[1024];
tr_snprintf( result, sizeof( result ), "gotMetadataFromURL: http error %ld: %s",
response_code, tr_webGetResponseStr( response_code ) );
tr_idle_function_done( data->data, result );
}
tr_free( data );
}
static bool
isCurlURL( const char * filename )
{
if( filename == NULL )
return false;
return !strncmp( filename, "ftp://", 6 ) ||
!strncmp( filename, "http://", 7 ) ||
!strncmp( filename, "https://", 8 );
}
static tr_file_index_t*
fileListFromList( tr_benc * list, tr_file_index_t * setmeCount )
{
size_t i;
const size_t childCount = tr_bencListSize( list );
tr_file_index_t n = 0;
tr_file_index_t * files = tr_new0( tr_file_index_t, childCount );
for( i=0; i<childCount; ++i ) {
int64_t intVal;
if( tr_bencGetInt( tr_bencListChild( list, i ), &intVal ) )
files[n++] = (tr_file_index_t)intVal;
}
*setmeCount = n;
return files;
}
static const char*
torrentAdd( tr_session * session,
tr_benc * args_in,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data )
{
const char * filename = NULL;
const char * metainfo_base64 = NULL;
assert( idle_data != NULL );
tr_bencDictFindStr( args_in, "filename", &filename );
tr_bencDictFindStr( args_in, "metainfo", &metainfo_base64 );
if( !filename && !metainfo_base64 )
return "no filename or metainfo specified";
else
{
int64_t i;
bool boolVal;
tr_benc * l;
const char * str;
const char * cookies = NULL;
tr_ctor * ctor = tr_ctorNew( session );
/* set the optional arguments */
tr_bencDictFindStr( args_in, "cookies", &cookies );
if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
tr_ctorSetDownloadDir( ctor, TR_FORCE, str );
if( tr_bencDictFindBool( args_in, "paused", &boolVal ) )
tr_ctorSetPaused( ctor, TR_FORCE, boolVal );
if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
tr_ctorSetPeerLimit( ctor, TR_FORCE, i );
if( tr_bencDictFindInt( args_in, "bandwidthPriority", &i ) )
tr_ctorSetBandwidthPriority( ctor, i );
if( tr_bencDictFindList( args_in, "files-unwanted", &l ) ) {
tr_file_index_t fileCount;
tr_file_index_t * files = fileListFromList( l, &fileCount );
tr_ctorSetFilesWanted( ctor, files, fileCount, false );
tr_free( files );
}
if( tr_bencDictFindList( args_in, "files-wanted", &l ) ) {
tr_file_index_t fileCount;
tr_file_index_t * files = fileListFromList( l, &fileCount );
tr_ctorSetFilesWanted( ctor, files, fileCount, true );
tr_free( files );
}
if( tr_bencDictFindList( args_in, "priority-low", &l ) ) {
tr_file_index_t fileCount;
tr_file_index_t * files = fileListFromList( l, &fileCount );
tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_LOW );
tr_free( files );
}
if( tr_bencDictFindList( args_in, "priority-normal", &l ) ) {
tr_file_index_t fileCount;
tr_file_index_t * files = fileListFromList( l, &fileCount );
tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_NORMAL );
tr_free( files );
}
if( tr_bencDictFindList( args_in, "priority-high", &l ) ) {
tr_file_index_t fileCount;
tr_file_index_t * files = fileListFromList( l, &fileCount );
tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_HIGH );
tr_free( files );
}
dbgmsg( "torrentAdd: filename is \"%s\"", filename ? filename : "(null)" );
if( isCurlURL( filename ) )
{
struct add_torrent_idle_data * d = tr_new0( struct add_torrent_idle_data, 1 );
d->data = idle_data;
d->ctor = ctor;
tr_webRun( session, filename, NULL, cookies, gotMetadataFromURL, d );
}
else
{
char * fname = tr_strstrip( tr_strdup( filename ) );
if( fname == NULL )
{
int len;
char * metainfo = tr_base64_decode( metainfo_base64, -1, &len );
tr_ctorSetMetainfo( ctor, (uint8_t*)metainfo, len );
tr_free( metainfo );
}
else if( !strncmp( fname, "magnet:?", 8 ) )
{
tr_ctorSetMetainfoFromMagnetLink( ctor, fname );
}
else
{
tr_ctorSetMetainfoFromFile( ctor, fname );
}
addTorrentImpl( idle_data, ctor );
tr_free( fname );
}
}
return NULL;
}
/***
****
***/
static const char*
sessionSet( tr_session * session,
tr_benc * args_in,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data UNUSED )
{
int64_t i;
double d;
bool boolVal;
const char * str;
assert( idle_data == NULL );
if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, &i ) )
tr_sessionSetCacheLimit_MB( session, i );
if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_UP_KBps, &i ) )
tr_sessionSetAltSpeed_KBps( session, TR_UP, i );
if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, &i ) )
tr_sessionSetAltSpeed_KBps( session, TR_DOWN, i );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_ENABLED, &boolVal ) )
tr_sessionUseAltSpeed( session, boolVal );
if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &i ) )
tr_sessionSetAltSpeedBegin( session, i );
if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_END, &i ) )
tr_sessionSetAltSpeedEnd( session, i );
if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &i ) )
tr_sessionSetAltSpeedDay( session, i );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &boolVal ) )
tr_sessionUseAltSpeedTime( session, boolVal );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_BLOCKLIST_ENABLED, &boolVal ) )
tr_blocklistSetEnabled( session, boolVal );
if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_BLOCKLIST_URL, &str ) )
tr_blocklistSetURL( session, str );
if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
tr_sessionSetDownloadDir( session, str );
if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_QUEUE_STALLED_MINUTES, &i ) )
tr_sessionSetQueueStalledMinutes( session, i );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_QUEUE_STALLED_ENABLED, &boolVal ) )
tr_sessionSetQueueStalledEnabled( session, boolVal );
if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_DOWNLOAD_QUEUE_SIZE, &i ) )
tr_sessionSetQueueSize( session, TR_DOWN, i );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DOWNLOAD_QUEUE_ENABLED, &boolVal ) )
tr_sessionSetQueueEnabled ( session, TR_DOWN, boolVal );
if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_INCOMPLETE_DIR, &str ) )
tr_sessionSetIncompleteDir( session, str );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, &boolVal ) )
tr_sessionSetIncompleteDirEnabled( session, boolVal );
if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &i ) )
tr_sessionSetPeerLimit( session, i );
if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i ) )
tr_sessionSetPeerLimitPerTorrent( session, i );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) )
tr_sessionSetPexEnabled( session, boolVal );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DHT_ENABLED, &boolVal ) )
tr_sessionSetDHTEnabled( session, boolVal );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_UTP_ENABLED, &boolVal ) )
tr_sessionSetUTPEnabled( session, boolVal );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_LPD_ENABLED, &boolVal ) )
tr_sessionSetLPDEnabled( session, boolVal );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, &boolVal ) )
tr_sessionSetPeerPortRandomOnStart( session, boolVal );
if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_PORT, &i ) )
tr_sessionSetPeerPort( session, i );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) )
tr_sessionSetPortForwardingEnabled( session, boolVal );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_RENAME_PARTIAL_FILES, &boolVal ) )
tr_sessionSetIncompleteFileNamingEnabled( session, boolVal );
if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
tr_sessionSetRatioLimit( session, d );
if( tr_bencDictFindBool( args_in, "seedRatioLimited", &boolVal ) )
tr_sessionSetRatioLimited( session, boolVal );
if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_IDLE_LIMIT, &i ) )
tr_sessionSetIdleLimit( session, i );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, &boolVal ) )
tr_sessionSetIdleLimited( session, boolVal );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_START, &boolVal ) )
tr_sessionSetPaused( session, !boolVal );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_SEED_QUEUE_ENABLED, &boolVal ) )
tr_sessionSetQueueEnabled ( session, TR_UP, boolVal );
if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_SEED_QUEUE_SIZE, &i ) )
tr_sessionSetQueueSize( session, TR_UP, i );
if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, &str ) )
tr_sessionSetTorrentDoneScript( session, str );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, &boolVal ) )
tr_sessionSetTorrentDoneScriptEnabled( session, boolVal );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_TRASH_ORIGINAL, &boolVal ) )
tr_sessionSetDeleteSource( session, boolVal );
if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_DSPEED_KBps, &i ) )
tr_sessionSetSpeedLimit_KBps( session, TR_DOWN, i );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DSPEED_ENABLED, &boolVal ) )
tr_sessionLimitSpeed( session, TR_DOWN, boolVal );
if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_USPEED_KBps, &i ) )
tr_sessionSetSpeedLimit_KBps( session, TR_UP, i );
if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_USPEED_ENABLED, &boolVal ) )
tr_sessionLimitSpeed( session, TR_UP, boolVal );
if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_ENCRYPTION, &str ) ) {
if( !strcmp( str, "required" ) )
tr_sessionSetEncryption( session, TR_ENCRYPTION_REQUIRED );
else if( !strcmp( str, "tolerated" ) )
tr_sessionSetEncryption( session, TR_CLEAR_PREFERRED );
else
tr_sessionSetEncryption( session, TR_ENCRYPTION_PREFERRED );
}
notify( session, TR_RPC_SESSION_CHANGED, NULL );
return NULL;
}
static const char*
sessionStats( tr_session * session,
tr_benc * args_in UNUSED,
tr_benc * args_out,
struct tr_rpc_idle_data * idle_data UNUSED )
{
int running = 0;
int total = 0;
tr_benc * d;
tr_session_stats currentStats = { 0.0f, 0, 0, 0, 0, 0 };
tr_session_stats cumulativeStats = { 0.0f, 0, 0, 0, 0, 0 };
tr_torrent * tor = NULL;
assert( idle_data == NULL );
while(( tor = tr_torrentNext( session, tor ))) {
++total;
if( tor->isRunning )
++running;
}
tr_sessionGetStats( session, &currentStats );
tr_sessionGetCumulativeStats( session, &cumulativeStats );
tr_bencDictAddInt ( args_out, "activeTorrentCount", running );
tr_bencDictAddReal( args_out, "downloadSpeed", tr_sessionGetPieceSpeed_Bps( session, TR_DOWN ) );
tr_bencDictAddInt ( args_out, "pausedTorrentCount", total - running );
tr_bencDictAddInt ( args_out, "torrentCount", total );
tr_bencDictAddReal( args_out, "uploadSpeed", tr_sessionGetPieceSpeed_Bps( session, TR_UP ) );
d = tr_bencDictAddDict( args_out, "cumulative-stats", 5 );
tr_bencDictAddInt( d, "downloadedBytes", cumulativeStats.downloadedBytes );
tr_bencDictAddInt( d, "filesAdded", cumulativeStats.filesAdded );
tr_bencDictAddInt( d, "secondsActive", cumulativeStats.secondsActive );
tr_bencDictAddInt( d, "sessionCount", cumulativeStats.sessionCount );
tr_bencDictAddInt( d, "uploadedBytes", cumulativeStats.uploadedBytes );
d = tr_bencDictAddDict( args_out, "current-stats", 5 );
tr_bencDictAddInt( d, "downloadedBytes", currentStats.downloadedBytes );
tr_bencDictAddInt( d, "filesAdded", currentStats.filesAdded );
tr_bencDictAddInt( d, "secondsActive", currentStats.secondsActive );
tr_bencDictAddInt( d, "sessionCount", currentStats.sessionCount );
tr_bencDictAddInt( d, "uploadedBytes", currentStats.uploadedBytes );
return NULL;
}
static const char*
sessionGet( tr_session * s,
tr_benc * args_in UNUSED,
tr_benc * args_out,
struct tr_rpc_idle_data * idle_data UNUSED )
{
const char * str;
tr_benc * d = args_out;
assert( idle_data == NULL );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP_KBps, tr_sessionGetAltSpeed_KBps(s,TR_UP) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, tr_sessionGetAltSpeed_KBps(s,TR_DOWN) );
tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED, tr_sessionUsesAltSpeed(s) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, tr_sessionGetAltSpeedBegin(s) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,tr_sessionGetAltSpeedEnd(s) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,tr_sessionGetAltSpeedDay(s) );
tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, tr_sessionUsesAltSpeedTime(s) );
tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, tr_blocklistIsEnabled( s ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_BLOCKLIST_URL, tr_blocklistGetURL( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, tr_sessionGetCacheLimit_MB( s ) );
tr_bencDictAddInt ( d, "blocklist-size", tr_blocklistGetRuleCount( s ) );
tr_bencDictAddStr ( d, "config-dir", tr_sessionGetConfigDir( s ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR, tr_sessionGetDownloadDir( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_DOWNLOAD_QUEUE_ENABLED, tr_sessionGetQueueEnabled( s, TR_DOWN ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_DOWNLOAD_QUEUE_SIZE, tr_sessionGetQueueSize( s, TR_DOWN ) );
tr_bencDictAddInt ( d, "download-dir-free-space", tr_sessionGetDownloadDirFreeSpace( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, tr_sessionGetPeerLimit( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT, tr_sessionGetPeerLimitPerTorrent( s ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR, tr_sessionGetIncompleteDir( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, tr_sessionIsIncompleteDirEnabled( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED, tr_sessionIsPexEnabled( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_UTP_ENABLED, tr_sessionIsUTPEnabled( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED, tr_sessionIsDHTEnabled( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED, tr_sessionIsLPDEnabled( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT, tr_sessionGetPeerPort( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, tr_sessionGetPeerPortRandomOnStart( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES, tr_sessionIsIncompleteFileNamingEnabled( s ) );
tr_bencDictAddInt ( d, "rpc-version", RPC_VERSION );
tr_bencDictAddInt ( d, "rpc-version-minimum", RPC_VERSION_MIN );
tr_bencDictAddReal( d, "seedRatioLimit", tr_sessionGetRatioLimit( s ) );
tr_bencDictAddBool( d, "seedRatioLimited", tr_sessionIsRatioLimited( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT, tr_sessionGetIdleLimit( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, tr_sessionIsIdleLimited( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_SEED_QUEUE_ENABLED, tr_sessionGetQueueEnabled( s, TR_UP ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_SEED_QUEUE_SIZE, tr_sessionGetQueueSize( s, TR_UP ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_START, !tr_sessionGetPaused( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL, tr_sessionGetDeleteSource( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_UP ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_UP ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_DOWN ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_DOWN ) );
tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, tr_sessionGetTorrentDoneScript( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, tr_sessionIsTorrentDoneScriptEnabled( s ) );
tr_bencDictAddInt ( d, TR_PREFS_KEY_QUEUE_STALLED_MINUTES, tr_sessionGetQueueStalledMinutes( s ) );
tr_bencDictAddBool( d, TR_PREFS_KEY_QUEUE_STALLED_ENABLED, tr_sessionGetQueueStalledEnabled( s ) );
tr_formatter_get_units( tr_bencDictAddDict( d, "units", 0 ) );
tr_bencDictAddStr ( d, "version", LONG_VERSION_STRING );
switch( tr_sessionGetEncryption( s ) ) {
case TR_CLEAR_PREFERRED: str = "tolerated"; break;
case TR_ENCRYPTION_REQUIRED: str = "required"; break;
default: str = "preferred"; break;
}
tr_bencDictAddStr( d, TR_PREFS_KEY_ENCRYPTION, str );
return NULL;
}
/***
****
***/
static const char*
sessionClose( tr_session * session,
tr_benc * args_in UNUSED,
tr_benc * args_out UNUSED,
struct tr_rpc_idle_data * idle_data UNUSED )
{
notify( session, TR_RPC_SESSION_CLOSE, NULL );
return NULL;
}
/***
****
***/
typedef const char* ( *handler )( tr_session*, tr_benc*, tr_benc*, struct tr_rpc_idle_data * );
static struct method
{
const char * name;
bool immediate;
handler func;
}
methods[] =
{
{ "port-test", false, portTest },
{ "blocklist-update", false, blocklistUpdate },
{ "session-close", true, sessionClose },
{ "session-get", true, sessionGet },
{ "session-set", true, sessionSet },
{ "session-stats", true, sessionStats },
{ "torrent-add", false, torrentAdd },
{ "torrent-get", true, torrentGet },
{ "torrent-remove", true, torrentRemove },
{ "torrent-set", true, torrentSet },
{ "torrent-set-location", true, torrentSetLocation },
{ "torrent-start", true, torrentStart },
{ "torrent-start-now", true, torrentStartNow },
{ "torrent-stop", true, torrentStop },
{ "torrent-verify", true, torrentVerify },
{ "torrent-reannounce", true, torrentReannounce },
{ "queue-move-top", true, queueMoveTop },
{ "queue-move-up", true, queueMoveUp },
{ "queue-move-down", true, queueMoveDown },
{ "queue-move-bottom", true, queueMoveBottom }
};
static void
noop_response_callback( tr_session * session UNUSED,
struct evbuffer * response UNUSED,
void * user_data UNUSED )
{
}
static void
request_exec( tr_session * session,
tr_benc * request,
tr_rpc_response_func callback,
void * callback_user_data )
{
int i;
const char * str;
tr_benc * args_in = tr_bencDictFind( request, "arguments" );
const char * result = NULL;
if( callback == NULL )
callback = noop_response_callback;
/* parse the request */
if( !tr_bencDictFindStr( request, "method", &str ) )
result = "no method name";
else {
const int n = TR_N_ELEMENTS( methods );
for( i = 0; i < n; ++i )
if( !strcmp( str, methods[i].name ) )
break;
if( i ==n )
result = "method name not recognized";
}
/* if we couldn't figure out which method to use, return an error */
if( result != NULL )
{
int64_t tag;
tr_benc response;
struct evbuffer * buf;
tr_bencInitDict( &response, 3 );
tr_bencDictAddDict( &response, "arguments", 0 );
tr_bencDictAddStr( &response, "result", result );
if( tr_bencDictFindInt( request, "tag", &tag ) )
tr_bencDictAddInt( &response, "tag", tag );
buf = tr_bencToBuf( &response, TR_FMT_JSON_LEAN );
(*callback)( session, buf, callback_user_data );
evbuffer_free( buf );
tr_bencFree( &response );
}
else if( methods[i].immediate )
{
int64_t tag;
tr_benc response;
tr_benc * args_out;
struct evbuffer * buf;
tr_bencInitDict( &response, 3 );
args_out = tr_bencDictAddDict( &response, "arguments", 0 );
result = (*methods[i].func)( session, args_in, args_out, NULL );
if( result == NULL )
result = "success";
tr_bencDictAddStr( &response, "result", result );
if( tr_bencDictFindInt( request, "tag", &tag ) )
tr_bencDictAddInt( &response, "tag", tag );
buf = tr_bencToBuf( &response, TR_FMT_JSON_LEAN );
(*callback)( session, buf, callback_user_data );
evbuffer_free( buf );
tr_bencFree( &response );
}
else
{
int64_t tag;
struct tr_rpc_idle_data * data = tr_new0( struct tr_rpc_idle_data, 1 );
data->session = session;
data->response = tr_new0( tr_benc, 1 );
tr_bencInitDict( data->response, 3 );
if( tr_bencDictFindInt( request, "tag", &tag ) )
tr_bencDictAddInt( data->response, "tag", tag );
data->args_out = tr_bencDictAddDict( data->response, "arguments", 0 );
data->callback = callback;
data->callback_user_data = callback_user_data;
(*methods[i].func)( session, args_in, data->args_out, data );
}
}
void
tr_rpc_request_exec_json( tr_session * session,
const void * request_json,
int request_len,
tr_rpc_response_func callback,
void * callback_user_data )
{
tr_benc top;
int have_content;
if( request_len < 0 )
request_len = strlen( request_json );
have_content = !tr_jsonParse( "rpc", request_json, request_len, &top, NULL );
request_exec( session, have_content ? &top : NULL, callback, callback_user_data );
if( have_content )
tr_bencFree( &top );
}
/**
* Munge the URI into a usable form.
*
* We have very loose typing on this to make the URIs as simple as possible:
* - anything not a 'tag' or 'method' is automatically in 'arguments'
* - values that are all-digits are numbers
* - values that are all-digits or commas are number lists
* - all other values are strings
*/
void
tr_rpc_parse_list_str( tr_benc * setme,
const char * str,
int len )
{
int valueCount;
int * values = tr_parseNumberRange( str, len, &valueCount );
if( valueCount == 0 )
tr_bencInitStr( setme, str, len );
else if( valueCount == 1 )
tr_bencInitInt( setme, values[0] );
else {
int i;
tr_bencInitList( setme, valueCount );
for( i=0; i<valueCount; ++i )
tr_bencListAddInt( setme, values[i] );
}
tr_free( values );
}
void
tr_rpc_request_exec_uri( tr_session * session,
const void * request_uri,
int request_len,
tr_rpc_response_func callback,
void * callback_user_data )
{
tr_benc top, * args;
char * request = tr_strndup( request_uri, request_len );
const char * pch;
tr_bencInitDict( &top, 3 );
args = tr_bencDictAddDict( &top, "arguments", 0 );
pch = strchr( request, '?' );
if( !pch ) pch = request;
while( pch )
{
const char * delim = strchr( pch, '=' );
const char * next = strchr( pch, '&' );
if( delim )
{
char * key = tr_strndup( pch, delim - pch );
int isArg = strcmp( key, "method" ) && strcmp( key, "tag" );
tr_benc * parent = isArg ? args : &top;
tr_rpc_parse_list_str( tr_bencDictAdd( parent, key ),
delim + 1,
next ? (size_t)(
next -
( delim + 1 ) ) : strlen( delim + 1 ) );
tr_free( key );
}
pch = next ? next + 1 : NULL;
}
request_exec( session, &top, callback, callback_user_data );
/* cleanup */
tr_bencFree( &top );
tr_free( request );
}