source: trunk/libtransmission/rpcimpl.c @ 8121

Last change on this file since 8121 was 8121, checked in by charles, 13 years ago

(trunk rpc) added "fileStats" to rpc s.t. there's a simple interface for getting all the torrent's stat fields

  • Property svn:keywords set to Date Rev Author Id
File size: 43.9 KB
Line 
1/*
2 * This file Copyright (C) 2008-2009 Charles Kerr <charles@transmissionbt.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: rpcimpl.c 8121 2009-03-31 22:13:43Z charles $
11 */
12
13#include <assert.h>
14#include <ctype.h> /* isdigit */
15#include <stdlib.h> /* strtol */
16#include <string.h> /* strcmp */
17
18#include <event.h> /* evbuffer */
19
20#include "transmission.h"
21#include "bencode.h"
22#include "rpcimpl.h"
23#include "json.h"
24#include "session.h"
25#include "stats.h"
26#include "torrent.h"
27#include "completion.h"
28#include "utils.h"
29#include "web.h"
30
31#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) )
32
33#if 0
34#define dbgmsg(fmt, ...) \
35    do { \
36        fprintf( stderr, "%s:%d"#fmt, __FILE__, __LINE__, __VA_ARGS__ ); \
37        fprintf( stderr, "\n" ); \
38    } while( 0 )
39#else
40#define dbgmsg( ... ) \
41    do { \
42        if( tr_deepLoggingIsActive( ) ) \
43            tr_deepLog( __FILE__, __LINE__, "RPC", __VA_ARGS__ ); \
44    } while( 0 )
45#endif
46
47
48/***
49****
50***/
51
52static tr_rpc_callback_status
53notify( tr_session * session,
54        int          type,
55        tr_torrent * tor )
56{
57    tr_rpc_callback_status status = 0;
58
59    if( session->rpc_func )
60        status = session->rpc_func( session, type, tor,
61                                    session->rpc_func_user_data );
62
63    return status;
64}
65
66/***
67****
68***/
69
70/* For functions that can't be immediately executed, like torrentAdd,
71 * this is the callback data used to pass a response to the caller
72 * when the task is complete */
73struct tr_rpc_idle_data
74{
75    tr_session            * session;
76    tr_benc               * response;
77    tr_benc               * args_out;
78    tr_rpc_response_func    callback;
79    void                  * callback_user_data;
80};
81
82static void
83tr_idle_function_done( struct tr_rpc_idle_data * data, const char * result )
84{
85    struct evbuffer * buf = tr_getBuffer( );
86
87    if( result == NULL )
88        result = "success";
89    tr_bencDictAddStr( data->response, "result", result );
90
91    tr_bencSaveAsJSON( data->response, buf );
92    (*data->callback)( data->session, (const char*)EVBUFFER_DATA(buf),
93                       EVBUFFER_LENGTH(buf), data->callback_user_data );
94
95    tr_releaseBuffer( buf );
96    tr_bencFree( data->response );
97    tr_free( data->response );
98    tr_free( data );
99}
100
101/***
102****
103***/
104
105static tr_torrent **
106getTorrents( tr_session * session,
107             tr_benc    * args,
108             int        * setmeCount )
109{
110    int           torrentCount = 0;
111    int64_t       id;
112    tr_torrent ** torrents = NULL;
113    tr_benc *     ids;
114    const char * str;
115
116    if( tr_bencDictFindList( args, "ids", &ids ) )
117    {
118        int       i;
119        const int n = tr_bencListSize( ids );
120
121        torrents = tr_new0( tr_torrent *, n );
122
123        for( i = 0; i < n; ++i )
124        {
125            tr_torrent * tor = NULL;
126            tr_benc *    node = tr_bencListChild( ids, i );
127            const char * str;
128            if( tr_bencGetInt( node, &id ) )
129                tor = tr_torrentFindFromId( session, id );
130            else if( tr_bencGetStr( node, &str ) )
131                tor = tr_torrentFindFromHashString( session, str );
132            if( tor )
133                torrents[torrentCount++] = tor;
134        }
135    }
136    else if( tr_bencDictFindInt( args, "ids", &id )
137           || tr_bencDictFindInt( args, "id", &id ) )
138    {
139        tr_torrent * tor;
140        torrents = tr_new0( tr_torrent *, 1 );
141        if( ( tor = tr_torrentFindFromId( session, id ) ) )
142            torrents[torrentCount++] = tor;
143    }
144    else if( tr_bencDictFindStr( args, "ids", &str ) )
145    {
146        if( !strcmp( str, "recently-active" ) )
147        {
148            tr_torrent * tor = NULL;
149            const time_t now = time( NULL );
150            const time_t window = 60;
151            const int n = tr_sessionCountTorrents( session );
152            torrents = tr_new0( tr_torrent *, n );
153            while( ( tor = tr_torrentNext( session, tor ) ) )
154                if( tor->anyDate >= now - window )
155                    torrents[torrentCount++] = tor;
156        }
157    }
158    else /* all of them */
159    {
160        tr_torrent * tor = NULL;
161        const int    n = tr_sessionCountTorrents( session );
162        torrents = tr_new0( tr_torrent *, n );
163        while( ( tor = tr_torrentNext( session, tor ) ) )
164            torrents[torrentCount++] = tor;
165    }
166
167    *setmeCount = torrentCount;
168    return torrents;
169}
170
171static const char*
172torrentStart( tr_session               * session,
173              tr_benc                  * args_in,
174              tr_benc                  * args_out UNUSED,
175              struct tr_rpc_idle_data  * idle_data )
176{
177    int           i, torrentCount;
178    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
179
180    assert( idle_data == NULL );
181
182    for( i = 0; i < torrentCount; ++i )
183    {
184        tr_torrent * tor = torrents[i];
185        tr_torrentStart( tor );
186        notify( session, TR_RPC_TORRENT_STARTED, tor );
187    }
188    tr_free( torrents );
189    return NULL;
190}
191
192static const char*
193torrentStop( tr_session               * session,
194             tr_benc                  * args_in,
195             tr_benc                  * args_out UNUSED,
196             struct tr_rpc_idle_data  * idle_data )
197{
198    int           i, torrentCount;
199    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
200
201    assert( idle_data == NULL );
202
203    for( i = 0; i < torrentCount; ++i )
204    {
205        tr_torrent * tor = torrents[i];
206        tr_torrentStop( tor );
207        notify( session, TR_RPC_TORRENT_STOPPED, tor );
208    }
209    tr_free( torrents );
210    return NULL;
211}
212
213static const char*
214torrentRemove( tr_session               * session,
215               tr_benc                  * args_in,
216               tr_benc                  * args_out UNUSED,
217               struct tr_rpc_idle_data  * idle_data )
218{
219    int i;
220    int torrentCount;
221    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
222
223    assert( idle_data == NULL );
224
225    for( i=0; i<torrentCount; ++i )
226    {
227        tr_torrent * tor = torrents[i];
228        const tr_rpc_callback_status status = notify( session, TR_RPC_TORRENT_REMOVING, tor );
229        tr_bool deleteFlag;
230        if( tr_bencDictFindBool( args_in, "delete-local-data", &deleteFlag ) && deleteFlag )
231            tr_torrentDeleteLocalData( tor, NULL );
232        if( !( status & TR_RPC_NOREMOVE ) )
233            tr_torrentRemove( tor );
234    }
235
236    tr_free( torrents );
237    return NULL;
238}
239
240static const char*
241torrentReannounce( tr_session               * session,
242                   tr_benc                  * args_in,
243                   tr_benc                  * args_out UNUSED,
244                   struct tr_rpc_idle_data  * idle_data )
245{
246    int i, torrentCount;
247    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
248
249    assert( idle_data == NULL );
250
251    for( i=0; i<torrentCount; ++i )
252    {
253        tr_torrent * tor = torrents[i];
254        if( tr_torrentCanManualUpdate( tor ) )
255        {
256            tr_torrentManualUpdate( tor );
257            notify( session, TR_RPC_TORRENT_CHANGED, tor );
258        }
259    }
260
261    tr_free( torrents );
262    return NULL;
263}
264
265static const char*
266torrentVerify( tr_session               * session,
267               tr_benc                  * args_in,
268               tr_benc                  * args_out UNUSED,
269               struct tr_rpc_idle_data  * idle_data )
270{
271    int           i, torrentCount;
272    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
273
274    assert( idle_data == NULL );
275
276    for( i = 0; i < torrentCount; ++i )
277    {
278        tr_torrent * tor = torrents[i];
279        tr_torrentVerify( tor );
280        notify( session, TR_RPC_TORRENT_CHANGED, tor );
281    }
282
283    tr_free( torrents );
284    return NULL;
285}
286
287/***
288****
289***/
290
291static void
292addFileStats( const tr_torrent * tor, tr_benc * list )
293{
294    tr_file_index_t i;
295    tr_file_index_t n;
296    const tr_info * info = tr_torrentInfo( tor );
297    tr_file_stat * files = tr_torrentFiles( tor, &n );
298
299    for( i = 0; i < info->fileCount; ++i )
300    {
301        const tr_file * file = &info->files[i];
302        tr_benc * d = tr_bencListAddDict( list, 3 );
303        tr_bencDictAddInt( d, "bytesCompleted", files[i].bytesCompleted );
304        tr_bencDictAddInt( d, "priority", file->priority );
305        tr_bencDictAddBool( d, "wanted", !file->dnd );
306    }
307
308    tr_torrentFilesFree( files, n );
309}
310
311static void
312addFiles( const tr_torrent * tor,
313          tr_benc *          list )
314{
315    tr_file_index_t i;
316    tr_file_index_t n;
317    const tr_info * info = tr_torrentInfo( tor );
318    tr_file_stat *  files = tr_torrentFiles( tor, &n );
319
320    for( i = 0; i < info->fileCount; ++i )
321    {
322        const tr_file * file = &info->files[i];
323        tr_benc *       d = tr_bencListAddDict( list, 3 );
324        tr_bencDictAddInt( d, "bytesCompleted", files[i].bytesCompleted );
325        tr_bencDictAddInt( d, "length", file->length );
326        tr_bencDictAddStr( d, "name", file->name );
327    }
328
329    tr_torrentFilesFree( files, n );
330}
331
332static void
333addWebseeds( const tr_info * info,
334             tr_benc *       webseeds )
335{
336    int i;
337
338    for( i = 0; i < info->webseedCount; ++i )
339        tr_bencListAddStr( webseeds, info->webseeds[i] );
340}
341
342static void
343addTrackers( const tr_info * info,
344             tr_benc *       trackers )
345{
346    int i;
347
348    for( i = 0; i < info->trackerCount; ++i )
349    {
350        const tr_tracker_info * t = &info->trackers[i];
351        tr_benc *               d = tr_bencListAddDict( trackers, 3 );
352        tr_bencDictAddStr( d, "announce", t->announce );
353        tr_bencDictAddStr( d, "scrape", t->scrape );
354        tr_bencDictAddInt( d, "tier", t->tier );
355    }
356}
357
358static void
359addPeers( const tr_torrent * tor,
360          tr_benc *          list )
361{
362    int            i;
363    int            peerCount;
364    tr_peer_stat * peers = tr_torrentPeers( tor, &peerCount );
365
366    tr_bencInitList( list, peerCount );
367
368    for( i = 0; i < peerCount; ++i )
369    {
370        tr_benc *            d = tr_bencListAddDict( list, 14 );
371        const tr_peer_stat * peer = peers + i;
372        tr_bencDictAddStr ( d, "address", peer->addr );
373        tr_bencDictAddStr ( d, "clientName", peer->client );
374        tr_bencDictAddBool( d, "clientIsChoked", peer->clientIsChoked );
375        tr_bencDictAddBool( d, "clientIsInterested", peer->clientIsInterested );
376        tr_bencDictAddStr ( d, "flagStr", peer->flagStr );
377        tr_bencDictAddBool( d, "isDownloadingFrom", peer->isDownloadingFrom );
378        tr_bencDictAddBool( d, "isEncrypted", peer->isEncrypted );
379        tr_bencDictAddBool( d, "isIncoming", peer->isIncoming );
380        tr_bencDictAddBool( d, "isUploadingTo", peer->isUploadingTo );
381        tr_bencDictAddBool( d, "peerIsChoked", peer->peerIsChoked );
382        tr_bencDictAddBool( d, "peerIsInterested", peer->peerIsInterested );
383        tr_bencDictAddInt ( d, "port", peer->port );
384        tr_bencDictAddReal( d, "progress", peer->progress );
385        tr_bencDictAddInt ( d, "rateToClient", (int)( peer->rateToClient * 1024.0 ) );
386        tr_bencDictAddInt ( d, "rateToPeer", (int)( peer->rateToPeer * 1024.0 ) );
387    }
388
389    tr_torrentPeersFree( peers, peerCount );
390}
391
392static void
393addField( const tr_torrent * tor,
394          tr_benc *          d,
395          const char *       key )
396{
397    const tr_info * inf = tr_torrentInfo( tor );
398    const tr_stat * st = tr_torrentStat( (tr_torrent*)tor );
399
400    if( !strcmp( key, "activityDate" ) )
401        tr_bencDictAddInt( d, key, st->activityDate );
402    else if( !strcmp( key, "addedDate" ) )
403        tr_bencDictAddInt( d, key, st->addedDate );
404    else if( !strcmp( key, "announceResponse" ) )
405        tr_bencDictAddStr( d, key, st->announceResponse );
406    else if( !strcmp( key, "announceURL" ) )
407        tr_bencDictAddStr( d, key, st->announceURL );
408    else if( !strcmp( key, "comment" ) )
409        tr_bencDictAddStr( d, key, inf->comment ? inf->comment : "" );
410    else if( !strcmp( key, "corruptEver" ) )
411        tr_bencDictAddInt( d, key, st->corruptEver );
412    else if( !strcmp( key, "creator" ) )
413        tr_bencDictAddStr( d, key, inf->creator ? inf->creator : "" );
414    else if( !strcmp( key, "dateCreated" ) )
415        tr_bencDictAddInt( d, key, inf->dateCreated );
416    else if( !strcmp( key, "desiredAvailable" ) )
417        tr_bencDictAddInt( d, key, st->desiredAvailable );
418    else if( !strcmp( key, "doneDate" ) )
419        tr_bencDictAddInt( d, key, st->doneDate );
420    else if( !strcmp( key, "downloadDir" ) )
421        tr_bencDictAddStr( d, key, tr_torrentGetDownloadDir( tor ) );
422    else if( !strcmp( key, "downloadedEver" ) )
423        tr_bencDictAddInt( d, key, st->downloadedEver );
424    else if( !strcmp( key, "downloaders" ) )
425        tr_bencDictAddInt( d, key, st->downloaders );
426    else if( !strcmp( key, "downloadLimit" ) )
427        tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit( tor, TR_DOWN ) );
428    else if( !strcmp( key, "downloadLimited" ) )
429        tr_bencDictAddBool( d, key, tr_torrentUsesSpeedLimit( tor, TR_DOWN ) );
430    else if( !strcmp( key, "error" ) )
431        tr_bencDictAddInt( d, key, st->error );
432    else if( !strcmp( key, "errorString" ) )
433        tr_bencDictAddStr( d, key, st->errorString );
434    else if( !strcmp( key, "eta" ) )
435        tr_bencDictAddInt( d, key, st->eta );
436    else if( !strcmp( key, "files" ) )
437        addFiles( tor, tr_bencDictAddList( d, key, inf->fileCount ) );
438    else if( !strcmp( key, "fileStats" ) )
439        addFileStats( tor, tr_bencDictAddList( d, key, inf->fileCount ) );
440    else if( !strcmp( key, "hashString" ) )
441        tr_bencDictAddStr( d, key, tor->info.hashString );
442    else if( !strcmp( key, "haveUnchecked" ) )
443        tr_bencDictAddInt( d, key, st->haveUnchecked );
444    else if( !strcmp( key, "haveValid" ) )
445        tr_bencDictAddInt( d, key, st->haveValid );
446    else if( !strcmp( key, "honorsSessionLimits" ) )
447        tr_bencDictAddBool( d, key, tr_torrentUsesSessionLimits( tor ) );
448    else if( !strcmp( key, "id" ) )
449        tr_bencDictAddInt( d, key, st->id );
450    else if( !strcmp( key, "isPrivate" ) )
451        tr_bencDictAddBool( d, key, tr_torrentIsPrivate( tor ) );
452    else if( !strcmp( key, "lastAnnounceTime" ) )
453        tr_bencDictAddInt( d, key, st->lastAnnounceTime );
454    else if( !strcmp( key, "lastScrapeTime" ) )
455        tr_bencDictAddInt( d, key, st->lastScrapeTime );
456    else if( !strcmp( key, "leechers" ) )
457        tr_bencDictAddInt( d, key, st->leechers );
458    else if( !strcmp( key, "leftUntilDone" ) )
459        tr_bencDictAddInt( d, key, st->leftUntilDone );
460    else if( !strcmp( key, "manualAnnounceTime" ) )
461        tr_bencDictAddInt( d, key, st->manualAnnounceTime );
462    else if( !strcmp( key, "maxConnectedPeers" ) )
463        tr_bencDictAddInt( d, key,  tr_torrentGetPeerLimit( tor ) );
464    else if( !strcmp( key, "name" ) )
465        tr_bencDictAddStr( d, key, inf->name );
466    else if( !strcmp( key, "nextAnnounceTime" ) )
467        tr_bencDictAddInt( d, key, st->nextAnnounceTime );
468    else if( !strcmp( key, "nextScrapeTime" ) )
469        tr_bencDictAddInt( d, key, st->nextScrapeTime );
470    else if( !strcmp( key, "percentDone" ) )
471        tr_bencDictAddReal( d, key, st->percentDone );
472    else if( !strcmp( key, "peer-limit" ) )
473        tr_bencDictAddInt( d, key, tr_torrentGetPeerLimit( tor ) );
474    else if( !strcmp( key, "peers" ) )
475        addPeers( tor, tr_bencDictAdd( d, key ) );
476    else if( !strcmp( key, "peersConnected" ) )
477        tr_bencDictAddInt( d, key, st->peersConnected );
478    else if( !strcmp( key, "peersFrom" ) )
479    {
480        tr_benc *   tmp = tr_bencDictAddDict( d, key, 4 );
481        const int * f = st->peersFrom;
482        tr_bencDictAddInt( tmp, "fromCache",    f[TR_PEER_FROM_CACHE] );
483        tr_bencDictAddInt( tmp, "fromIncoming", f[TR_PEER_FROM_INCOMING] );
484        tr_bencDictAddInt( tmp, "fromPex",      f[TR_PEER_FROM_PEX] );
485        tr_bencDictAddInt( tmp, "fromTracker",  f[TR_PEER_FROM_TRACKER] );
486    }
487    else if( !strcmp( key, "peersGettingFromUs" ) )
488        tr_bencDictAddInt( d, key, st->peersGettingFromUs );
489    else if( !strcmp( key, "peersKnown" ) )
490        tr_bencDictAddInt( d, key, st->peersKnown );
491    else if( !strcmp( key, "peersSendingToUs" ) )
492        tr_bencDictAddInt( d, key, st->peersSendingToUs );
493    else if( !strcmp( key, "pieces" ) ) {
494        const tr_bitfield * pieces = tr_cpPieceBitfield( &tor->completion );
495        char * str = tr_base64_encode( pieces->bits, pieces->byteCount, NULL );
496        tr_bencDictAddStr( d, key, str );
497        tr_free( str );
498    }
499    else if( !strcmp( key, "pieceCount" ) )
500        tr_bencDictAddInt( d, key, inf->pieceCount );
501    else if( !strcmp( key, "pieceSize" ) )
502        tr_bencDictAddInt( d, key, inf->pieceSize );
503    else if( !strcmp( key, "priorities" ) )
504    {
505        tr_file_index_t i;
506        tr_benc *       p = tr_bencDictAddList( d, key, inf->fileCount );
507        for( i = 0; i < inf->fileCount; ++i )
508            tr_bencListAddInt( p, inf->files[i].priority );
509    }
510    else if( !strcmp( key, "rateDownload" ) )
511        tr_bencDictAddInt( d, key, (int)( st->pieceDownloadSpeed * 1024 ) );
512    else if( !strcmp( key, "rateUpload" ) )
513        tr_bencDictAddInt( d, key, (int)( st->pieceUploadSpeed * 1024 ) );
514    else if( !strcmp( key, "ratio" ) )
515        tr_bencDictAddReal( d, key, st->ratio );
516    else if( !strcmp( key, "recheckProgress" ) )
517        tr_bencDictAddReal( d, key, st->recheckProgress );
518    else if( !strcmp( key, "scrapeResponse" ) )
519        tr_bencDictAddStr( d, key, st->scrapeResponse );
520    else if( !strcmp( key, "scrapeURL" ) )
521        tr_bencDictAddStr( d, key, st->scrapeURL );
522    else if( !strcmp( key, "seeders" ) )
523        tr_bencDictAddInt( d, key, st->seeders );
524    else if( !strcmp( key, "seedRatioLimit" ) )
525        tr_bencDictAddReal( d, key, tr_torrentGetRatioLimit( tor ) );
526    else if( !strcmp( key, "seedRatioMode" ) )
527        tr_bencDictAddInt( d, key, tr_torrentGetRatioMode( tor ) );
528    else if( !strcmp( key, "sizeWhenDone" ) )
529        tr_bencDictAddInt( d, key, st->sizeWhenDone );
530    else if( !strcmp( key, "startDate" ) )
531        tr_bencDictAddInt( d, key, st->startDate );
532    else if( !strcmp( key, "status" ) )
533        tr_bencDictAddInt( d, key, st->activity );
534    else if( !strcmp( key, "swarmSpeed" ) )
535        tr_bencDictAddInt( d, key, (int)( st->swarmSpeed * 1024 ) );
536    else if( !strcmp( key, "timesCompleted" ) )
537        tr_bencDictAddInt( d, key, st->timesCompleted );
538    else if( !strcmp( key, "trackers" ) )
539        addTrackers( inf, tr_bencDictAddList( d, key, inf->trackerCount ) );
540    else if( !strcmp( key, "totalSize" ) )
541        tr_bencDictAddInt( d, key, inf->totalSize );
542    else if( !strcmp( key, "uploadedEver" ) )
543        tr_bencDictAddInt( d, key, st->uploadedEver );
544    else if( !strcmp( key, "uploadRatio" ) )
545        tr_bencDictAddReal( d, key, tr_getRatio( st->uploadedEver, st->downloadedEver ) );
546    else if( !strcmp( key, "uploadLimit" ) )
547        tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit( tor, TR_UP ) );
548    else if( !strcmp( key, "uploadLimited" ) )
549        tr_bencDictAddBool( d, key, tr_torrentUsesSpeedLimit( tor, TR_UP ) );
550    else if( !strcmp( key, "wanted" ) )
551    {
552        tr_file_index_t i;
553        tr_benc *       w = tr_bencDictAddList( d, key, inf->fileCount );
554        for( i = 0; i < inf->fileCount; ++i )
555            tr_bencListAddInt( w, inf->files[i].dnd ? 0 : 1 );
556    }
557    else if( !strcmp( key, "webseeds" ) )
558        addWebseeds( inf, tr_bencDictAddList( d, key, inf->trackerCount ) );
559    else if( !strcmp( key, "webseedsSendingToUs" ) )
560        tr_bencDictAddInt( d, key, st->webseedsSendingToUs );
561}
562
563static void
564addInfo( const tr_torrent * tor,
565         tr_benc *          d,
566         tr_benc *          fields )
567{
568    int          i;
569    const int    n = tr_bencListSize( fields );
570    const char * str;
571
572    tr_bencInitDict( d, n );
573
574    for( i = 0; i < n; ++i )
575        if( tr_bencGetStr( tr_bencListChild( fields, i ), &str ) )
576            addField( tor, d, str );
577}
578
579static const char*
580torrentGet( tr_session               * session,
581            tr_benc                  * args_in,
582            tr_benc                  * args_out,
583            struct tr_rpc_idle_data  * idle_data )
584{
585    int           i, torrentCount;
586    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
587    tr_benc *     list = tr_bencDictAddList( args_out, "torrents", torrentCount );
588    tr_benc *     fields;
589    const char *  msg = NULL;
590
591    assert( idle_data == NULL );
592
593    if( !tr_bencDictFindList( args_in, "fields", &fields ) )
594        msg = "no fields specified";
595    else for( i = 0; i < torrentCount; ++i )
596            addInfo( torrents[i], tr_bencListAdd( list ), fields );
597
598    tr_free( torrents );
599    return msg;
600}
601
602/***
603****
604***/
605
606static const char*
607setFilePriorities( tr_torrent * tor,
608                   int          priority,
609                   tr_benc *    list )
610{
611    int i;
612    int64_t tmp;
613    int fileCount = 0;
614    const int n = tr_bencListSize( list );
615    const char * errmsg = NULL;
616    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
617
618    if( n )
619    {
620        for( i = 0; i < n; ++i ) {
621            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) {
622                if( 0 <= tmp && tmp < tor->info.fileCount ) {
623                    files[fileCount++] = tmp;
624                } else {
625                    errmsg = "file index out of range";
626                }
627            }
628        }
629    }
630    else /* if empty set, apply to all */
631    {
632        tr_file_index_t t;
633        for( t = 0; t < tor->info.fileCount; ++t )
634            files[fileCount++] = t;
635    }
636
637    if( fileCount )
638        tr_torrentSetFilePriorities( tor, files, fileCount, priority );
639
640    tr_free( files );
641    return errmsg;
642}
643
644static const char*
645setFileDLs( tr_torrent * tor,
646            int          do_download,
647            tr_benc *    list )
648{
649    int i;
650    int64_t tmp;
651    int fileCount = 0;
652    const int n = tr_bencListSize( list );
653    const char * errmsg = NULL;
654    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
655
656    if( n ) /* if argument list, process them */
657    {
658        for( i = 0; i < n; ++i ) {
659            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) {
660                if( 0 <= tmp && tmp < tor->info.fileCount ) {
661                    files[fileCount++] = tmp;
662                } else {
663                    errmsg = "file index out of range";
664                }
665            }
666        }
667    }
668    else /* if empty set, apply to all */
669    {
670        tr_file_index_t t;
671        for( t = 0; t < tor->info.fileCount; ++t )
672            files[fileCount++] = t;
673    }
674
675    if( fileCount )
676        tr_torrentSetFileDLs( tor, files, fileCount, do_download );
677
678    tr_free( files );
679    return errmsg;
680}
681
682static const char*
683torrentSet( tr_session               * session,
684            tr_benc                  * args_in,
685            tr_benc                  * args_out UNUSED,
686            struct tr_rpc_idle_data  * idle_data )
687{
688    const char * errmsg = NULL;
689    int i, torrentCount;
690    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
691
692    assert( idle_data == NULL );
693
694    for( i = 0; i < torrentCount; ++i )
695    {
696        int64_t      tmp;
697        double       d;
698        tr_benc *    files;
699        tr_bool      boolVal;
700        tr_torrent * tor = torrents[i];
701
702        if( tr_bencDictFindList( args_in, "files-unwanted", &files ) )
703            setFileDLs( tor, FALSE, files );
704        if( tr_bencDictFindList( args_in, "files-wanted", &files ) )
705            setFileDLs( tor, TRUE, files );
706        if( tr_bencDictFindInt( args_in, "peer-limit", &tmp ) )
707            tr_torrentSetPeerLimit( tor, tmp );
708        if( !errmsg &&  tr_bencDictFindList( args_in, "priority-high", &files ) )
709            errmsg = setFilePriorities( tor, TR_PRI_HIGH, files );
710        if( !errmsg && tr_bencDictFindList( args_in, "priority-low", &files ) )
711            errmsg = setFilePriorities( tor, TR_PRI_LOW, files );
712        if( !errmsg && tr_bencDictFindList( args_in, "priority-normal", &files ) )
713            errmsg = setFilePriorities( tor, TR_PRI_NORMAL, files );
714        if( tr_bencDictFindInt( args_in, "downloadLimit", &tmp ) )
715            tr_torrentSetSpeedLimit( tor, TR_DOWN, tmp );
716        if( tr_bencDictFindBool( args_in, "downloadLimited", &boolVal ) )
717            tr_torrentUseSpeedLimit( tor, TR_DOWN, boolVal );
718        if( tr_bencDictFindBool( args_in, "honorsSessionLimits", &boolVal ) )
719            tr_torrentUseSessionLimits( tor, boolVal );
720        if( tr_bencDictFindInt( args_in, "uploadLimit", &tmp ) )
721            tr_torrentSetSpeedLimit( tor, TR_UP, tmp );
722        if( tr_bencDictFindBool( args_in, "uploadLimited", &boolVal ) )
723            tr_torrentUseSpeedLimit( tor, TR_UP, boolVal );
724        if( tr_bencDictFindReal( args_in, "ratio-limit", &d ) )
725            tr_torrentSetRatioLimit( tor, d );
726        if( tr_bencDictFindInt( args_in, "ratio-limit-mode", &tmp ) )
727            tr_torrentSetRatioMode( tor, tmp );
728        if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
729            tr_torrentSetRatioLimit( tor, d );
730        if( tr_bencDictFindInt( args_in, "seedRatioMode", &tmp ) )
731            tr_torrentSetRatioMode( tor, tmp );
732        notify( session, TR_RPC_TORRENT_CHANGED, tor );
733    }
734
735    tr_free( torrents );
736    return errmsg;
737}
738
739/***
740****
741***/
742
743static void
744addTorrentImpl( struct tr_rpc_idle_data * data, tr_ctor * ctor )
745{
746    int err = 0;
747    const char * result = NULL;
748    tr_torrent * tor = tr_torrentNew( data->session, ctor, &err );
749
750    tr_ctorFree( ctor );
751
752    if( tor )
753    {
754        tr_benc fields;
755        tr_bencInitList( &fields, 3 );
756        tr_bencListAddStr( &fields, "id" );
757        tr_bencListAddStr( &fields, "name" );
758        tr_bencListAddStr( &fields, "hashString" );
759        addInfo( tor, tr_bencDictAdd( data->args_out, "torrent-added" ), &fields );
760        notify( data->session, TR_RPC_TORRENT_ADDED, tor );
761        tr_bencFree( &fields );
762    }
763    else if( err == TR_EDUPLICATE )
764    {
765        result = "duplicate torrent";
766    }
767    else if( err == TR_EINVALID )
768    {
769        result = "invalid or corrupt torrent file";
770    }
771
772    tr_idle_function_done( data, result );
773}
774
775
776struct add_torrent_idle_data
777{
778    struct tr_rpc_idle_data * data;
779    tr_ctor * ctor;
780};
781
782static void
783gotMetadataFromURL( tr_session       * session UNUSED,
784                    long               response_code,
785                    const void       * response,
786                    size_t             response_byte_count,
787                    void             * user_data )
788{
789    struct add_torrent_idle_data * data = user_data;
790
791    dbgmsg( "torrentAdd: HTTP response code was %ld (%s); response length was %zu bytes",
792            response_code, tr_webGetResponseStr( response_code ), response_byte_count );
793
794    if( response_code == 200 )
795    {
796        tr_ctorSetMetainfo( data->ctor, response, response_byte_count );
797        addTorrentImpl( data->data, data->ctor );
798    }
799    else
800    {
801        char result[1024];
802        tr_snprintf( result, sizeof( result ), "http error %ld: %s",
803                     response_code, tr_webGetResponseStr( response_code ) );
804        tr_idle_function_done( data->data, result );
805    }
806
807    tr_free( data );
808}
809
810static tr_bool
811isCurlURL( const char * filename )
812{
813    if( filename == NULL )
814        return FALSE;
815
816    return ( strstr( filename, "ftp://" ) != NULL )
817        || ( strstr( filename, "http://" ) != NULL )
818        || ( strstr( filename, "https://" ) != NULL );
819}
820
821static const char*
822torrentAdd( tr_session               * session,
823            tr_benc                  * args_in,
824            tr_benc                  * args_out UNUSED,
825            struct tr_rpc_idle_data  * idle_data )
826{
827    const char * filename = NULL;
828    const char * metainfo_base64 = NULL;
829
830    assert( idle_data != NULL );
831
832    tr_bencDictFindStr( args_in, "filename", &filename );
833    tr_bencDictFindStr( args_in, "metainfo", &metainfo_base64 );
834    if( !filename && !metainfo_base64 )
835        return "no filename or metainfo specified";
836    else
837    {
838        int64_t      i;
839        tr_bool      boolVal;
840        const char * str;
841        tr_ctor    * ctor = tr_ctorNew( session );
842
843        /* set the optional arguments */
844        if( tr_bencDictFindStr( args_in, "download-dir", &str ) )
845            tr_ctorSetDownloadDir( ctor, TR_FORCE, str );
846        if( tr_bencDictFindBool( args_in, "paused", &boolVal ) )
847            tr_ctorSetPaused( ctor, TR_FORCE, boolVal );
848        if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
849            tr_ctorSetPeerLimit( ctor, TR_FORCE, i );
850
851        dbgmsg( "torrentAdd: filename is \"%s\"", filename );
852
853        if( isCurlURL( filename ) )
854        {
855            struct add_torrent_idle_data * d = tr_new0( struct add_torrent_idle_data, 1 );
856            d->data = idle_data;
857            d->ctor = ctor;
858            tr_webRun( session, filename, NULL, gotMetadataFromURL, d );
859        }
860        else
861        {
862            if( filename != NULL )
863                tr_ctorSetMetainfoFromFile( ctor, filename );
864            else {
865                int len;
866                char * metainfo = tr_base64_decode( metainfo_base64, -1,  &len );
867                tr_ctorSetMetainfo( ctor, (uint8_t*)metainfo, len );
868                tr_free( metainfo );
869            }
870            addTorrentImpl( idle_data, ctor );
871        }
872
873    }
874
875    return NULL;
876}
877
878/***
879****
880***/
881
882static const char*
883sessionSet( tr_session               * session,
884            tr_benc                  * args_in,
885            tr_benc                  * args_out UNUSED,
886            struct tr_rpc_idle_data  * idle_data )
887{
888    int64_t      i;
889    double       d;
890    tr_bool      boolVal;
891    const char * str;
892
893    assert( idle_data == NULL );
894
895    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_UP, &i ) )
896        tr_sessionSetAltSpeed( session, TR_UP, i );
897    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_DOWN, &i ) )
898        tr_sessionSetAltSpeed( session, TR_DOWN, i );
899    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_ENABLED, &boolVal ) )
900        tr_sessionUseAltSpeed( session, boolVal );
901    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &i ) )
902        tr_sessionSetAltSpeedBegin( session, i );
903    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_END, &i ) )
904        tr_sessionSetAltSpeedEnd( session, i );
905    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &boolVal ) )
906        tr_sessionUseAltSpeedTime( session, boolVal );
907    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_BLOCKLIST_ENABLED, &boolVal ) )
908        tr_blocklistSetEnabled( session, boolVal );
909    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
910        tr_sessionSetDownloadDir( session, str );
911    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &i ) )
912        tr_sessionSetPeerLimit( session, i );
913    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i ) )
914        tr_sessionSetPeerLimitPerTorrent( session, i );
915    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) )
916        tr_sessionSetPexEnabled( session, boolVal );
917    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_PORT, &i ) )
918        tr_sessionSetPeerPort( session, i );
919    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) )
920        tr_sessionSetPortForwardingEnabled( session, boolVal );
921    if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
922        tr_sessionSetRatioLimit( session, d );
923    if( tr_bencDictFindBool( args_in, "seedRatioLimited", &boolVal ) )
924        tr_sessionSetRatioLimited( session, boolVal );
925    if( tr_bencDictFindInt( args_in, "speed-limit-down", &i ) )
926        tr_sessionSetSpeedLimit( session, TR_DOWN, i );
927    if( tr_bencDictFindBool( args_in, "speed-limit-down-enabled", &boolVal ) )
928        tr_sessionLimitSpeed( session, TR_DOWN, boolVal );
929    if( tr_bencDictFindInt( args_in, "speed-limit-up", &i ) )
930        tr_sessionSetSpeedLimit( session, TR_UP, i );
931    if( tr_bencDictFindBool( args_in, "speed-limit-up-enabled", &boolVal ) )
932        tr_sessionLimitSpeed( session, TR_UP, boolVal );
933    if( tr_bencDictFindReal( args_in, "ratio-limit", &d ) )
934        tr_sessionSetRatioLimit( session, d );
935    if( tr_bencDictFindBool( args_in, "ratio-limit-enabled", &boolVal ) )
936        tr_sessionSetRatioLimited( session, boolVal );
937    if( tr_bencDictFindStr( args_in, "encryption", &str ) ) {
938        if( !strcmp( str, "required" ) )
939            tr_sessionSetEncryption( session, TR_ENCRYPTION_REQUIRED );
940        else if( !strcmp( str, "tolerated" ) )
941            tr_sessionSetEncryption( session, TR_CLEAR_PREFERRED );
942        else
943            tr_sessionSetEncryption( session, TR_ENCRYPTION_PREFERRED );
944    }
945
946    notify( session, TR_RPC_SESSION_CHANGED, NULL );
947
948    return NULL;
949}
950
951static const char*
952sessionStats( tr_session               * session,
953              tr_benc                  * args_in UNUSED,
954              tr_benc                  * args_out,
955              struct tr_rpc_idle_data  * idle_data )
956{
957    int running = 0;
958    int total = 0;
959    tr_benc * d; 
960    tr_session_stats currentStats = { 0.0f, 0, 0, 0, 0, 0 }; 
961    tr_session_stats cumulativeStats = { 0.0f, 0, 0, 0, 0, 0 }; 
962    tr_torrent * tor = NULL;
963
964    assert( idle_data == NULL );
965
966    while(( tor = tr_torrentNext( session, tor ))) {
967        ++total;
968        if( tor->isRunning )
969            ++running;
970    }
971
972    tr_sessionGetStats( session, &currentStats ); 
973    tr_sessionGetCumulativeStats( session, &cumulativeStats ); 
974
975    tr_bencDictAddInt( args_out, "activeTorrentCount", running );
976    tr_bencDictAddInt( args_out, "downloadSpeed", (int)( tr_sessionGetPieceSpeed( session, TR_DOWN ) * 1024 ) );
977    tr_bencDictAddInt( args_out, "pausedTorrentCount", total - running );
978    tr_bencDictAddInt( args_out, "torrentCount", total );
979    tr_bencDictAddInt( args_out, "uploadSpeed", (int)( tr_sessionGetPieceSpeed( session, TR_UP ) * 1024 ) );
980
981    d = tr_bencDictAddDict( args_out, "cumulative-stats", 5 ); 
982    tr_bencDictAddInt( d, "downloadedBytes", cumulativeStats.downloadedBytes ); 
983    tr_bencDictAddInt( d, "filesAdded", cumulativeStats.filesAdded ); 
984    tr_bencDictAddInt( d, "secondsActive", cumulativeStats.secondsActive ); 
985    tr_bencDictAddInt( d, "sessionCount", cumulativeStats.sessionCount ); 
986    tr_bencDictAddInt( d, "uploadedBytes", cumulativeStats.uploadedBytes ); 
987
988    d = tr_bencDictAddDict( args_out, "current-stats", 5 ); 
989    tr_bencDictAddInt( d, "downloadedBytes", currentStats.downloadedBytes ); 
990    tr_bencDictAddInt( d, "filesAdded", currentStats.filesAdded ); 
991    tr_bencDictAddInt( d, "secondsActive", currentStats.secondsActive ); 
992    tr_bencDictAddInt( d, "sessionCount", currentStats.sessionCount ); 
993    tr_bencDictAddInt( d, "uploadedBytes", currentStats.uploadedBytes ); 
994
995    return NULL;
996}
997
998static const char*
999sessionGet( tr_session               * s,
1000            tr_benc                  * args_in UNUSED,
1001            tr_benc                  * args_out,
1002            struct tr_rpc_idle_data  * idle_data )
1003{
1004    const char * str;
1005    tr_benc *    d = args_out;
1006
1007    assert( idle_data == NULL );
1008    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP, tr_sessionGetAltSpeed(s,TR_UP) );
1009    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN, tr_sessionGetAltSpeed(s,TR_DOWN) );
1010    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED, tr_sessionUsesAltSpeed(s) );
1011    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, tr_sessionGetAltSpeedBegin(s) );
1012    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,tr_sessionGetAltSpeedEnd(s) );
1013    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, tr_sessionUsesAltSpeedTime(s) );
1014    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, tr_blocklistIsEnabled( s ) );
1015    tr_bencDictAddInt ( d, "blocklist-size", tr_blocklistGetRuleCount( s ) );
1016    tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR, tr_sessionGetDownloadDir( s ) );
1017    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, tr_sessionGetPeerLimit( s ) );
1018    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT, tr_sessionGetPeerLimitPerTorrent( s ) );
1019    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED, tr_sessionIsPexEnabled( s ) );
1020    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT, tr_sessionGetPeerPort( s ) );
1021    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) );
1022    tr_bencDictAddInt ( d, "rpc-version", 4 );
1023    tr_bencDictAddInt ( d, "rpc-version-minimum", 1 );
1024    tr_bencDictAddReal( d, "seedRatioLimit", tr_sessionGetRatioLimit( s ) );
1025    tr_bencDictAddBool( d, "seedRatioLimited", tr_sessionIsRatioLimited( s ) );
1026    tr_bencDictAddInt ( d, "speed-limit-up", tr_sessionGetSpeedLimit( s, TR_UP ) );
1027    tr_bencDictAddBool( d, "speed-limit-up-enabled", tr_sessionIsSpeedLimited( s, TR_UP ) );
1028    tr_bencDictAddInt ( d, "speed-limit-down", tr_sessionGetSpeedLimit( s, TR_DOWN ) );
1029    tr_bencDictAddBool( d, "speed-limit-down-enabled", tr_sessionIsSpeedLimited( s, TR_DOWN ) );
1030    tr_bencDictAddReal( d, "ratio-limit", tr_sessionGetRatioLimit( s ) );
1031    tr_bencDictAddBool( d, "ratio-limit-enabled", tr_sessionIsRatioLimited( s ) );
1032    tr_bencDictAddStr ( d, "version", LONG_VERSION_STRING );
1033    switch( tr_sessionGetEncryption( s ) ) {
1034        case TR_CLEAR_PREFERRED: str = "tolerated"; break;
1035        case TR_ENCRYPTION_REQUIRED: str = "required"; break; 
1036        default: str = "preferred"; break;
1037    }
1038    tr_bencDictAddStr( d, "encryption", str );
1039
1040    return NULL;
1041}
1042
1043/***
1044****
1045***/
1046
1047typedef const char* ( *handler )( tr_session*, tr_benc*, tr_benc*, struct tr_rpc_idle_data * );
1048
1049static struct method
1050{
1051    const char *  name;
1052    tr_bool       immediate;
1053    handler       func;
1054}
1055methods[] =
1056{
1057    { "session-get",        TRUE,  sessionGet          },
1058    { "session-set",        TRUE,  sessionSet          },
1059    { "session-stats",      TRUE,  sessionStats        },
1060    { "torrent-add",        FALSE, torrentAdd          },
1061    { "torrent-get",        TRUE,  torrentGet          },
1062    { "torrent-remove",     TRUE,  torrentRemove       },
1063    { "torrent-set",        TRUE,  torrentSet          },
1064    { "torrent-start",      TRUE,  torrentStart        },
1065    { "torrent-stop",       TRUE,  torrentStop         },
1066    { "torrent-verify",     TRUE,  torrentVerify       },
1067    { "torrent-reannounce", TRUE,  torrentReannounce   }
1068};
1069
1070static void
1071noop_response_callback( tr_session * session UNUSED,
1072                        const char * response UNUSED,
1073                        size_t       response_len UNUSED,
1074                        void       * user_data UNUSED )
1075{
1076}
1077
1078static void
1079request_exec( tr_session             * session,
1080              tr_benc                * request,
1081              tr_rpc_response_func     callback,
1082              void                   * callback_user_data )
1083{
1084    int i;
1085    const char * str;
1086    tr_benc * args_in = tr_bencDictFind( request, "arguments" );
1087    const char * result = NULL;
1088
1089    if( callback == NULL )
1090        callback = noop_response_callback;
1091
1092    /* parse the request */
1093    if( !tr_bencDictFindStr( request, "method", &str ) )
1094        result = "no method name";
1095    else {
1096        const int n = TR_N_ELEMENTS( methods );
1097        for( i = 0; i < n; ++i )
1098            if( !strcmp( str, methods[i].name ) )
1099                break;
1100        if( i ==n )
1101            result = "method name not recognized";
1102    }
1103
1104    /* if we couldn't figure out which method to use, return an error */
1105    if( result != NULL )
1106    {
1107        int64_t tag;
1108        tr_benc response;
1109        struct evbuffer * buf = tr_getBuffer( );
1110
1111        tr_bencInitDict( &response, 3 );
1112        tr_bencDictAddDict( &response, "arguments", 0 );
1113        tr_bencDictAddStr( &response, "result", result );
1114        if( tr_bencDictFindInt( request, "tag", &tag ) )
1115            tr_bencDictAddInt( &response, "tag", tag );
1116        tr_bencSaveAsJSON( &response, buf );
1117        (*callback)( session, (const char*)EVBUFFER_DATA(buf),
1118                     EVBUFFER_LENGTH( buf ), callback_user_data );
1119
1120        tr_releaseBuffer( buf );
1121        tr_bencFree( &response );
1122    }
1123    else if( methods[i].immediate )
1124    {
1125        int64_t tag;
1126        tr_benc response;
1127        tr_benc * args_out;
1128        struct evbuffer * buf = tr_getBuffer( );
1129
1130        tr_bencInitDict( &response, 3 );
1131        args_out = tr_bencDictAddDict( &response, "arguments", 0 );
1132        result = (*methods[i].func)( session, args_in, args_out, NULL );
1133        if( result == NULL )
1134            result = "success";
1135        tr_bencDictAddStr( &response, "result", result );
1136        if( tr_bencDictFindInt( request, "tag", &tag ) )
1137            tr_bencDictAddInt( &response, "tag", tag );
1138        tr_bencSaveAsJSON( &response, buf );
1139        (*callback)( session, (const char*)EVBUFFER_DATA(buf),
1140                     EVBUFFER_LENGTH(buf), callback_user_data );
1141
1142        tr_releaseBuffer( buf );
1143        tr_bencFree( &response );
1144    }
1145    else
1146    {
1147        int64_t tag;
1148        struct tr_rpc_idle_data * data = tr_new0( struct tr_rpc_idle_data, 1 );
1149        data->session = session;
1150        data->response = tr_new0( tr_benc, 1 );
1151        tr_bencInitDict( data->response, 3 );
1152        if( tr_bencDictFindInt( request, "tag", &tag ) )
1153            tr_bencDictAddInt( data->response, "tag", tag );
1154        data->args_out = tr_bencDictAddDict( data->response, "arguments", 0 );
1155        data->callback = callback;
1156        data->callback_user_data = callback_user_data;
1157        (*methods[i].func)( session, args_in, data->args_out, data );
1158    }
1159}
1160
1161void
1162tr_rpc_request_exec_json( tr_session            * session,
1163                          const void            * request_json,
1164                          int                     request_len,
1165                          tr_rpc_response_func    callback,
1166                          void                  * callback_user_data )
1167{
1168    tr_benc top;
1169    int have_content;
1170
1171    if( request_len < 0 )
1172        request_len = strlen( request_json );
1173
1174    have_content = !tr_jsonParse( request_json, request_len, &top, NULL );
1175    request_exec( session, have_content ? &top : NULL, callback, callback_user_data );
1176
1177    if( have_content )
1178        tr_bencFree( &top );
1179}
1180
1181/**
1182 * Munge the URI into a usable form.
1183 *
1184 * We have very loose typing on this to make the URIs as simple as possible:
1185 * - anything not a 'tag' or 'method' is automatically in 'arguments'
1186 * - values that are all-digits are numbers
1187 * - values that are all-digits or commas are number lists
1188 * - all other values are strings
1189 */
1190void
1191tr_rpc_parse_list_str( tr_benc     * setme,
1192                       const char  * str,
1193                       int           len )
1194
1195{
1196    int valueCount;
1197    int * values = tr_parseNumberRange( str, len, &valueCount );
1198
1199    if( valueCount == 0 )
1200        tr_bencInitStr( setme, str, len );
1201    else if( valueCount == 1 )
1202        tr_bencInitInt( setme, values[0] );
1203    else {
1204        int i;
1205        tr_bencInitList( setme, valueCount );
1206        for( i=0; i<valueCount; ++i )
1207            tr_bencListAddInt( setme, values[i] );
1208    }
1209
1210    tr_free( values );
1211}
1212
1213void
1214tr_rpc_request_exec_uri( tr_session           * session,
1215                         const void           * request_uri,
1216                         int                    request_len,
1217                         tr_rpc_response_func   callback,
1218                         void                 * callback_user_data )
1219{
1220    tr_benc      top, * args;
1221    char *       request = tr_strndup( request_uri, request_len );
1222    const char * pch;
1223
1224    tr_bencInitDict( &top, 3 );
1225    args = tr_bencDictAddDict( &top, "arguments", 0 );
1226
1227    pch = strchr( request, '?' );
1228    if( !pch ) pch = request;
1229    while( pch )
1230    {
1231        const char * delim = strchr( pch, '=' );
1232        const char * next = strchr( pch, '&' );
1233        if( delim )
1234        {
1235            char *    key = tr_strndup( pch, delim - pch );
1236            int       isArg = strcmp( key, "method" ) && strcmp( key, "tag" );
1237            tr_benc * parent = isArg ? args : &top;
1238            tr_rpc_parse_list_str( tr_bencDictAdd( parent, key ),
1239                                  delim + 1,
1240                                  next ? (size_t)(
1241                                       next -
1242                                      ( delim + 1 ) ) : strlen( delim + 1 ) );
1243            tr_free( key );
1244        }
1245        pch = next ? next + 1 : NULL;
1246    }
1247
1248    request_exec( session, &top, callback, callback_user_data );
1249
1250    /* cleanup */
1251    tr_bencFree( &top );
1252    tr_free( request );
1253}
Note: See TracBrowser for help on using the repository browser.