source: trunk/libtransmission/rpcimpl.c @ 11209

Last change on this file since 11209 was 11209, checked in by Longinus00, 12 years ago

switch trackerRemove and trackerReplace rpc calls to use tracker id instead of announce urls as identifiers

  • Property svn:keywords set to Date Rev Author Id
File size: 62.9 KB
Line 
1/*
2 * This file Copyright (C) 2008-2010 Mnemosyne LLC
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 11209 2010-09-14 06:23:48Z Longinus00 $
11 */
12
13#include <assert.h>
14#include <ctype.h> /* isdigit */
15#include <errno.h>
16#include <stdlib.h> /* strtol */
17#include <string.h> /* strcmp */
18#include <unistd.h> /* unlink */
19
20#include <event.h> /* evbuffer */
21
22#include "transmission.h"
23#include "bencode.h"
24#include "completion.h"
25#include "json.h"
26#include "rpcimpl.h"
27#include "session.h"
28#include "stats.h"
29#include "torrent.h"
30#include "utils.h"
31#include "version.h"
32#include "web.h"
33
34#define RPC_VERSION     10
35#define RPC_VERSION_MIN 1
36
37#define RECENTLY_ACTIVE_SECONDS 60
38
39#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) )
40
41#if 0
42#define dbgmsg(fmt, ...) \
43    do { \
44        fprintf( stderr, "%s:%d"#fmt, __FILE__, __LINE__, __VA_ARGS__ ); \
45        fprintf( stderr, "\n" ); \
46    } while( 0 )
47#else
48#define dbgmsg( ... ) \
49    do { \
50        if( tr_deepLoggingIsActive( ) ) \
51            tr_deepLog( __FILE__, __LINE__, "RPC", __VA_ARGS__ ); \
52    } while( 0 )
53#endif
54
55
56/***
57****
58***/
59
60static tr_rpc_callback_status
61notify( tr_session * session,
62        int          type,
63        tr_torrent * tor )
64{
65    tr_rpc_callback_status status = 0;
66
67    if( session->rpc_func )
68        status = session->rpc_func( session, type, tor,
69                                    session->rpc_func_user_data );
70
71    return status;
72}
73
74/***
75****
76***/
77
78/* For functions that can't be immediately executed, like torrentAdd,
79 * this is the callback data used to pass a response to the caller
80 * when the task is complete */
81struct tr_rpc_idle_data
82{
83    tr_session            * session;
84    tr_benc               * response;
85    tr_benc               * args_out;
86    tr_rpc_response_func    callback;
87    void                  * callback_user_data;
88};
89
90static void
91tr_idle_function_done( struct tr_rpc_idle_data * data, const char * result )
92{
93    struct evbuffer * buf = evbuffer_new( );
94
95    if( result == NULL )
96        result = "success";
97    tr_bencDictAddStr( data->response, "result", result );
98
99    tr_bencToBuf( data->response, TR_FMT_JSON_LEAN, buf );
100    (*data->callback)( data->session, (const char*)EVBUFFER_DATA(buf),
101                       EVBUFFER_LENGTH(buf), data->callback_user_data );
102
103    evbuffer_free( buf );
104    tr_bencFree( data->response );
105    tr_free( data->response );
106    tr_free( data );
107}
108
109/***
110****
111***/
112
113static tr_torrent **
114getTorrents( tr_session * session,
115             tr_benc    * args,
116             int        * setmeCount )
117{
118    int           torrentCount = 0;
119    int64_t       id;
120    tr_torrent ** torrents = NULL;
121    tr_benc *     ids;
122    const char * str;
123
124    if( tr_bencDictFindList( args, "ids", &ids ) )
125    {
126        int       i;
127        const int n = tr_bencListSize( ids );
128
129        torrents = tr_new0( tr_torrent *, n );
130
131        for( i = 0; i < n; ++i )
132        {
133            tr_torrent * tor = NULL;
134            tr_benc *    node = tr_bencListChild( ids, i );
135            const char * str;
136            if( tr_bencGetInt( node, &id ) )
137                tor = tr_torrentFindFromId( session, id );
138            else if( tr_bencGetStr( node, &str ) )
139                tor = tr_torrentFindFromHashString( session, str );
140            if( tor )
141                torrents[torrentCount++] = tor;
142        }
143    }
144    else if( tr_bencDictFindInt( args, "ids", &id )
145           || tr_bencDictFindInt( args, "id", &id ) )
146    {
147        tr_torrent * tor;
148        torrents = tr_new0( tr_torrent *, 1 );
149        if( ( tor = tr_torrentFindFromId( session, id ) ) )
150            torrents[torrentCount++] = tor;
151    }
152    else if( tr_bencDictFindStr( args, "ids", &str ) )
153    {
154        if( !strcmp( str, "recently-active" ) )
155        {
156            tr_torrent * tor = NULL;
157            const time_t now = tr_time( );
158            const time_t window = RECENTLY_ACTIVE_SECONDS;
159            const int n = tr_sessionCountTorrents( session );
160            torrents = tr_new0( tr_torrent *, n );
161            while( ( tor = tr_torrentNext( session, tor ) ) )
162                if( tor->anyDate >= now - window )
163                    torrents[torrentCount++] = tor;
164        }
165        else
166        {
167            tr_torrent * tor;
168            torrents = tr_new0( tr_torrent *, 1 );
169            if(( tor = tr_torrentFindFromHashString( session, str )))
170                torrents[torrentCount++] = tor;
171        }
172    }
173    else /* all of them */
174    {
175        tr_torrent * tor = NULL;
176        const int n = tr_sessionCountTorrents( session );
177        torrents = tr_new0( tr_torrent *, n );
178        while( ( tor = tr_torrentNext( session, tor ) ) )
179            torrents[torrentCount++] = tor;
180    }
181
182    *setmeCount = torrentCount;
183    return torrents;
184}
185
186static const char*
187torrentStart( tr_session               * session,
188              tr_benc                  * args_in,
189              tr_benc                  * args_out UNUSED,
190              struct tr_rpc_idle_data  * idle_data UNUSED )
191{
192    int           i, torrentCount;
193    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
194
195    assert( idle_data == NULL );
196
197    for( i = 0; i < torrentCount; ++i )
198    {
199        tr_torrent * tor = torrents[i];
200        if( !tor->isRunning )
201        {
202            tr_torrentStart( tor );
203            notify( session, TR_RPC_TORRENT_STARTED, tor );
204        }
205    }
206    tr_free( torrents );
207    return NULL;
208}
209
210static const char*
211torrentStop( tr_session               * session,
212             tr_benc                  * args_in,
213             tr_benc                  * args_out UNUSED,
214             struct tr_rpc_idle_data  * idle_data UNUSED )
215{
216    int           i, torrentCount;
217    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
218
219    assert( idle_data == NULL );
220
221    for( i = 0; i < torrentCount; ++i )
222    {
223        tr_torrent * tor = torrents[i];
224
225        if( tor->isRunning )
226        {
227            tor->isStopping = TRUE;
228            notify( session, TR_RPC_TORRENT_STOPPED, tor );
229        }
230    }
231    tr_free( torrents );
232    return NULL;
233}
234
235static const char*
236torrentRemove( tr_session               * session,
237               tr_benc                  * args_in,
238               tr_benc                  * args_out UNUSED,
239               struct tr_rpc_idle_data  * idle_data UNUSED )
240{
241    int i;
242    int torrentCount;
243    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
244
245    assert( idle_data == NULL );
246
247    for( i=0; i<torrentCount; ++i )
248    {
249        tr_torrent * tor = torrents[i];
250        const tr_rpc_callback_status status = notify( session, TR_RPC_TORRENT_REMOVING, tor );
251        tr_bool deleteFlag;
252        if( tr_bencDictFindBool( args_in, "delete-local-data", &deleteFlag ) && deleteFlag )
253            tr_torrentDeleteLocalData( tor, NULL );
254        if( !( status & TR_RPC_NOREMOVE ) )
255            tr_torrentRemove( tor );
256    }
257
258    tr_free( torrents );
259    return NULL;
260}
261
262static const char*
263torrentReannounce( tr_session               * session,
264                   tr_benc                  * args_in,
265                   tr_benc                  * args_out UNUSED,
266                   struct tr_rpc_idle_data  * idle_data UNUSED )
267{
268    int i, torrentCount;
269    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
270
271    assert( idle_data == NULL );
272
273    for( i=0; i<torrentCount; ++i )
274    {
275        tr_torrent * tor = torrents[i];
276        if( tr_torrentCanManualUpdate( tor ) )
277        {
278            tr_torrentManualUpdate( tor );
279            notify( session, TR_RPC_TORRENT_CHANGED, tor );
280        }
281    }
282
283    tr_free( torrents );
284    return NULL;
285}
286
287static const char*
288torrentVerify( tr_session               * session,
289               tr_benc                  * args_in,
290               tr_benc                  * args_out UNUSED,
291               struct tr_rpc_idle_data  * idle_data UNUSED )
292{
293    int           i, torrentCount;
294    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
295
296    assert( idle_data == NULL );
297
298    for( i = 0; i < torrentCount; ++i )
299    {
300        tr_torrent * tor = torrents[i];
301        tr_torrentVerify( tor );
302        notify( session, TR_RPC_TORRENT_CHANGED, tor );
303    }
304
305    tr_free( torrents );
306    return NULL;
307}
308
309/***
310****
311***/
312
313static void
314addFileStats( const tr_torrent * tor, tr_benc * list )
315{
316    tr_file_index_t i;
317    tr_file_index_t n;
318    const tr_info * info = tr_torrentInfo( tor );
319    tr_file_stat * files = tr_torrentFiles( tor, &n );
320
321    for( i = 0; i < info->fileCount; ++i )
322    {
323        const tr_file * file = &info->files[i];
324        tr_benc * d = tr_bencListAddDict( list, 3 );
325        tr_bencDictAddInt( d, "bytesCompleted", files[i].bytesCompleted );
326        tr_bencDictAddInt( d, "priority", file->priority );
327        tr_bencDictAddBool( d, "wanted", !file->dnd );
328    }
329
330    tr_torrentFilesFree( files, n );
331}
332
333static void
334addFiles( const tr_torrent * tor,
335          tr_benc *          list )
336{
337    tr_file_index_t i;
338    tr_file_index_t n;
339    const tr_info * info = tr_torrentInfo( tor );
340    tr_file_stat *  files = tr_torrentFiles( tor, &n );
341
342    for( i = 0; i < info->fileCount; ++i )
343    {
344        const tr_file * file = &info->files[i];
345        tr_benc *       d = tr_bencListAddDict( list, 3 );
346        tr_bencDictAddInt( d, "bytesCompleted", files[i].bytesCompleted );
347        tr_bencDictAddInt( d, "length", file->length );
348        tr_bencDictAddStr( d, "name", file->name );
349    }
350
351    tr_torrentFilesFree( files, n );
352}
353
354static void
355addWebseeds( const tr_info * info,
356             tr_benc *       webseeds )
357{
358    int i;
359
360    for( i = 0; i < info->webseedCount; ++i )
361        tr_bencListAddStr( webseeds, info->webseeds[i] );
362}
363
364static void
365addTrackers( const tr_info * info,
366             tr_benc *       trackers )
367{
368    int i;
369
370    for( i = 0; i < info->trackerCount; ++i )
371    {
372        const tr_tracker_info * t = &info->trackers[i];
373        tr_benc *               d = tr_bencListAddDict( trackers, 4 );
374        tr_bencDictAddStr( d, "announce", t->announce );
375        tr_bencDictAddInt( d, "id", t->id );
376        tr_bencDictAddStr( d, "scrape", t->scrape );
377        tr_bencDictAddInt( d, "tier", t->tier );
378    }
379}
380
381static void
382addTrackerStats( const tr_tracker_stat * st, int n, tr_benc * list )
383{
384    int i;
385
386    for( i=0; i<n; ++i )
387    {
388        const tr_tracker_stat * s = &st[i];
389        tr_benc * d = tr_bencListAddDict( list, 26 );
390        tr_bencDictAddStr ( d, "announce", s->announce );
391        tr_bencDictAddInt ( d, "announceState", s->announceState );
392        tr_bencDictAddInt ( d, "downloadCount", s->downloadCount );
393        tr_bencDictAddBool( d, "hasAnnounced", s->hasAnnounced );
394        tr_bencDictAddBool( d, "hasScraped", s->hasScraped );
395        tr_bencDictAddStr ( d, "host", s->host );
396        tr_bencDictAddInt ( d, "id", s->id );
397        tr_bencDictAddBool( d, "isBackup", s->isBackup );
398        tr_bencDictAddInt ( d, "lastAnnouncePeerCount", s->lastAnnouncePeerCount );
399        tr_bencDictAddStr ( d, "lastAnnounceResult", s->lastAnnounceResult );
400        tr_bencDictAddInt ( d, "lastAnnounceStartTime", s->lastAnnounceStartTime );
401        tr_bencDictAddBool( d, "lastAnnounceSucceeded", s->lastAnnounceSucceeded );
402        tr_bencDictAddInt ( d, "lastAnnounceTime", s->lastAnnounceTime );
403        tr_bencDictAddBool( d, "lastAnnounceTimedOut", s->lastAnnounceTimedOut );
404        tr_bencDictAddStr ( d, "lastScrapeResult", s->lastScrapeResult );
405        tr_bencDictAddInt ( d, "lastScrapeStartTime", s->lastScrapeStartTime );
406        tr_bencDictAddBool( d, "lastScrapeSucceeded", s->lastScrapeSucceeded );
407        tr_bencDictAddInt ( d, "lastScrapeTime", s->lastScrapeTime );
408        tr_bencDictAddInt ( d, "lastScrapeTimedOut", s->lastScrapeTimedOut );
409        tr_bencDictAddInt ( d, "leecherCount", s->leecherCount );
410        tr_bencDictAddInt ( d, "nextAnnounceTime", s->nextAnnounceTime );
411        tr_bencDictAddInt ( d, "nextScrapeTime", s->nextScrapeTime );
412        tr_bencDictAddStr ( d, "scrape", s->scrape );
413        tr_bencDictAddInt ( d, "scrapeState", s->scrapeState );
414        tr_bencDictAddInt ( d, "seederCount", s->seederCount );
415        tr_bencDictAddInt ( d, "tier", s->tier );
416    }
417}
418
419static void
420addPeers( const tr_torrent * tor,
421          tr_benc *          list )
422{
423    int            i;
424    int            peerCount;
425    tr_peer_stat * peers = tr_torrentPeers( tor, &peerCount );
426
427    tr_bencInitList( list, peerCount );
428
429    for( i = 0; i < peerCount; ++i )
430    {
431        tr_benc *            d = tr_bencListAddDict( list, 14 );
432        const tr_peer_stat * peer = peers + i;
433        tr_bencDictAddStr ( d, "address", peer->addr );
434        tr_bencDictAddStr ( d, "clientName", peer->client );
435        tr_bencDictAddBool( d, "clientIsChoked", peer->clientIsChoked );
436        tr_bencDictAddBool( d, "clientIsInterested", peer->clientIsInterested );
437        tr_bencDictAddStr ( d, "flagStr", peer->flagStr );
438        tr_bencDictAddBool( d, "isDownloadingFrom", peer->isDownloadingFrom );
439        tr_bencDictAddBool( d, "isEncrypted", peer->isEncrypted );
440        tr_bencDictAddBool( d, "isIncoming", peer->isIncoming );
441        tr_bencDictAddBool( d, "isUploadingTo", peer->isUploadingTo );
442        tr_bencDictAddBool( d, "peerIsChoked", peer->peerIsChoked );
443        tr_bencDictAddBool( d, "peerIsInterested", peer->peerIsInterested );
444        tr_bencDictAddInt ( d, "port", peer->port );
445        tr_bencDictAddReal( d, "progress", peer->progress );
446        tr_bencDictAddInt ( d, "rateToClient", toSpeedBytes( peer->rateToClient_KBps ) );
447        tr_bencDictAddInt ( d, "rateToPeer", toSpeedBytes( peer->rateToPeer_KBps ) );
448    }
449
450    tr_torrentPeersFree( peers, peerCount );
451}
452
453/* faster-than-strcmp() optimization.  this is kind of clumsy,
454   but addField() was in the profiler's top 10 list, and this
455   makes it 4x faster... */
456#define tr_streq(a,alen,b) ((alen+1==sizeof(b)) && !memcmp(a,b,alen))
457
458static void
459addField( const tr_torrent * tor, tr_benc * d, const char * key )
460{
461    const tr_info * inf = tr_torrentInfo( tor );
462    const tr_stat * st = tr_torrentStat( (tr_torrent*)tor );
463    const size_t keylen = strlen( key );
464
465    if( tr_streq( key, keylen, "activityDate" ) )
466        tr_bencDictAddInt( d, key, st->activityDate );
467    else if( tr_streq( key, keylen, "addedDate" ) )
468        tr_bencDictAddInt( d, key, st->addedDate );
469    else if( tr_streq( key, keylen, "bandwidthPriority" ) )
470        tr_bencDictAddInt( d, key, tr_torrentGetPriority( tor ) );
471    else if( tr_streq( key, keylen, "comment" ) )
472        tr_bencDictAddStr( d, key, inf->comment ? inf->comment : "" );
473    else if( tr_streq( key, keylen, "corruptEver" ) )
474        tr_bencDictAddInt( d, key, st->corruptEver );
475    else if( tr_streq( key, keylen, "creator" ) )
476        tr_bencDictAddStr( d, key, inf->creator ? inf->creator : "" );
477    else if( tr_streq( key, keylen, "dateCreated" ) )
478        tr_bencDictAddInt( d, key, inf->dateCreated );
479    else if( tr_streq( key, keylen, "desiredAvailable" ) )
480        tr_bencDictAddInt( d, key, st->desiredAvailable );
481    else if( tr_streq( key, keylen, "doneDate" ) )
482        tr_bencDictAddInt( d, key, st->doneDate );
483    else if( tr_streq( key, keylen, "downloadDir" ) )
484        tr_bencDictAddStr( d, key, tr_torrentGetDownloadDir( tor ) );
485    else if( tr_streq( key, keylen, "downloadedEver" ) )
486        tr_bencDictAddInt( d, key, st->downloadedEver );
487    else if( tr_streq( key, keylen, "downloadLimit" ) )
488        tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit_KBps( tor, TR_DOWN ) );
489    else if( tr_streq( key, keylen, "downloadLimited" ) )
490        tr_bencDictAddBool( d, key, tr_torrentUsesSpeedLimit( tor, TR_DOWN ) );
491    else if( tr_streq( key, keylen, "error" ) )
492        tr_bencDictAddInt( d, key, st->error );
493    else if( tr_streq( key, keylen, "errorString" ) )
494        tr_bencDictAddStr( d, key, st->errorString );
495    else if( tr_streq( key, keylen, "eta" ) )
496        tr_bencDictAddInt( d, key, st->eta );
497    else if( tr_streq( key, keylen, "files" ) )
498        addFiles( tor, tr_bencDictAddList( d, key, inf->fileCount ) );
499    else if( tr_streq( key, keylen, "fileStats" ) )
500        addFileStats( tor, tr_bencDictAddList( d, key, inf->fileCount ) );
501    else if( tr_streq( key, keylen, "hashString" ) )
502        tr_bencDictAddStr( d, key, tor->info.hashString );
503    else if( tr_streq( key, keylen, "haveUnchecked" ) )
504        tr_bencDictAddInt( d, key, st->haveUnchecked );
505    else if( tr_streq( key, keylen, "haveValid" ) )
506        tr_bencDictAddInt( d, key, st->haveValid );
507    else if( tr_streq( key, keylen, "honorsSessionLimits" ) )
508        tr_bencDictAddBool( d, key, tr_torrentUsesSessionLimits( tor ) );
509    else if( tr_streq( key, keylen, "id" ) )
510        tr_bencDictAddInt( d, key, st->id );
511    else if( tr_streq( key, keylen, "isFinished" ) )
512        tr_bencDictAddBool( d, key, st->finished );
513    else if( tr_streq( key, keylen, "isPrivate" ) )
514        tr_bencDictAddBool( d, key, tr_torrentIsPrivate( tor ) );
515    else if( tr_streq( key, keylen, "leftUntilDone" ) )
516        tr_bencDictAddInt( d, key, st->leftUntilDone );
517    else if( tr_streq( key, keylen, "manualAnnounceTime" ) )
518        tr_bencDictAddInt( d, key, st->manualAnnounceTime );
519    else if( tr_streq( key, keylen, "maxConnectedPeers" ) )
520        tr_bencDictAddInt( d, key,  tr_torrentGetPeerLimit( tor ) );
521    else if( tr_streq( key, keylen, "magnetLink" ) ) {
522        char * str = tr_torrentGetMagnetLink( tor );
523        tr_bencDictAddStr( d, key, str );
524        tr_free( str );
525    }
526    else if( tr_streq( key, keylen, "metadataPercentComplete" ) )
527        tr_bencDictAddReal( d, key, st->metadataPercentComplete );
528    else if( tr_streq( key, keylen, "name" ) )
529        tr_bencDictAddStr( d, key, inf->name );
530    else if( tr_streq( key, keylen, "percentDone" ) )
531        tr_bencDictAddReal( d, key, st->percentDone );
532    else if( tr_streq( key, keylen, "peer-limit" ) )
533        tr_bencDictAddInt( d, key, tr_torrentGetPeerLimit( tor ) );
534    else if( tr_streq( key, keylen, "peers" ) )
535        addPeers( tor, tr_bencDictAdd( d, key ) );
536    else if( tr_streq( key, keylen, "peersConnected" ) )
537        tr_bencDictAddInt( d, key, st->peersConnected );
538    else if( tr_streq( key, keylen, "peersFrom" ) )
539    {
540        tr_benc *   tmp = tr_bencDictAddDict( d, key, 6 );
541        const int * f = st->peersFrom;
542        tr_bencDictAddInt( tmp, "fromCache",    f[TR_PEER_FROM_RESUME] );
543        tr_bencDictAddInt( tmp, "fromDht",      f[TR_PEER_FROM_DHT] );
544        tr_bencDictAddInt( tmp, "fromIncoming", f[TR_PEER_FROM_INCOMING] );
545        tr_bencDictAddInt( tmp, "fromLtep",     f[TR_PEER_FROM_LTEP] );
546        tr_bencDictAddInt( tmp, "fromPex",      f[TR_PEER_FROM_PEX] );
547        tr_bencDictAddInt( tmp, "fromTracker",  f[TR_PEER_FROM_TRACKER] );
548    }
549    else if( tr_streq( key, keylen, "peersGettingFromUs" ) )
550        tr_bencDictAddInt( d, key, st->peersGettingFromUs );
551    else if( tr_streq( key, keylen, "peersKnown" ) )
552        tr_bencDictAddInt( d, key, st->peersKnown );
553    else if( tr_streq( key, keylen, "peersSendingToUs" ) )
554        tr_bencDictAddInt( d, key, st->peersSendingToUs );
555    else if( tr_streq( key, keylen, "pieces" ) ) {
556        const tr_bitfield * pieces = tr_cpPieceBitfield( &tor->completion );
557        char * str = tr_base64_encode( pieces->bits, pieces->byteCount, NULL );
558        tr_bencDictAddStr( d, key, str!=NULL ? str : "" );
559        tr_free( str );
560    }
561    else if( tr_streq( key, keylen, "pieceCount" ) )
562        tr_bencDictAddInt( d, key, inf->pieceCount );
563    else if( tr_streq( key, keylen, "pieceSize" ) )
564        tr_bencDictAddInt( d, key, inf->pieceSize );
565    else if( tr_streq( key, keylen, "priorities" ) )
566    {
567        tr_file_index_t i;
568        tr_benc *       p = tr_bencDictAddList( d, key, inf->fileCount );
569        for( i = 0; i < inf->fileCount; ++i )
570            tr_bencListAddInt( p, inf->files[i].priority );
571    }
572    else if( tr_streq( key, keylen, "rateDownload" ) )
573        tr_bencDictAddInt( d, key, toSpeedBytes( st->pieceDownloadSpeed_KBps ) );
574    else if( tr_streq( key, keylen, "rateUpload" ) )
575        tr_bencDictAddInt( d, key, toSpeedBytes( st->pieceUploadSpeed_KBps ) );
576    else if( tr_streq( key, keylen, "recheckProgress" ) )
577        tr_bencDictAddReal( d, key, st->recheckProgress );
578    else if( tr_streq( key, keylen, "seedIdleLimit" ) )
579        tr_bencDictAddInt( d, key, tr_torrentGetIdleLimit( tor ) );
580    else if( tr_streq( key, keylen, "seedIdleMode" ) )
581        tr_bencDictAddInt( d, key, tr_torrentGetIdleMode( tor ) );
582    else if( tr_streq( key, keylen, "seedRatioLimit" ) )
583        tr_bencDictAddReal( d, key, tr_torrentGetRatioLimit( tor ) );
584    else if( tr_streq( key, keylen, "seedRatioMode" ) )
585        tr_bencDictAddInt( d, key, tr_torrentGetRatioMode( tor ) );
586    else if( tr_streq( key, keylen, "sizeWhenDone" ) )
587        tr_bencDictAddInt( d, key, st->sizeWhenDone );
588    else if( tr_streq( key, keylen, "startDate" ) )
589        tr_bencDictAddInt( d, key, st->startDate );
590    else if( tr_streq( key, keylen, "status" ) )
591        tr_bencDictAddInt( d, key, st->activity );
592    else if( tr_streq( key, keylen, "trackers" ) )
593        addTrackers( inf, tr_bencDictAddList( d, key, inf->trackerCount ) );
594    else if( tr_streq( key, keylen, "trackerStats" ) ) {
595        int n;
596        tr_tracker_stat * s = tr_torrentTrackers( tor, &n );
597        addTrackerStats( s, n, tr_bencDictAddList( d, key, n ) );
598        tr_torrentTrackersFree( s, n );
599    }
600    else if( tr_streq( key, keylen, "torrentFile" ) )
601        tr_bencDictAddStr( d, key, inf->torrent );
602    else if( tr_streq( key, keylen, "totalSize" ) )
603        tr_bencDictAddInt( d, key, inf->totalSize );
604    else if( tr_streq( key, keylen, "uploadedEver" ) )
605        tr_bencDictAddInt( d, key, st->uploadedEver );
606    else if( tr_streq( key, keylen, "uploadLimit" ) )
607        tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit_KBps( tor, TR_UP ) );
608    else if( tr_streq( key, keylen, "uploadLimited" ) )
609        tr_bencDictAddBool( d, key, tr_torrentUsesSpeedLimit( tor, TR_UP ) );
610    else if( tr_streq( key, keylen, "uploadRatio" ) )
611        tr_bencDictAddReal( d, key, st->ratio );
612    else if( tr_streq( key, keylen, "wanted" ) )
613    {
614        tr_file_index_t i;
615        tr_benc *       w = tr_bencDictAddList( d, key, inf->fileCount );
616        for( i = 0; i < inf->fileCount; ++i )
617            tr_bencListAddInt( w, inf->files[i].dnd ? 0 : 1 );
618    }
619    else if( tr_streq( key, keylen, "webseeds" ) )
620        addWebseeds( inf, tr_bencDictAddList( d, key, inf->webseedCount ) );
621    else if( tr_streq( key, keylen, "webseedsSendingToUs" ) )
622        tr_bencDictAddInt( d, key, st->webseedsSendingToUs );
623}
624
625static void
626addInfo( const tr_torrent * tor,
627         tr_benc *          d,
628         tr_benc *          fields )
629{
630    int          i;
631    const int    n = tr_bencListSize( fields );
632    const char * str;
633
634    tr_bencInitDict( d, n );
635
636    for( i = 0; i < n; ++i )
637        if( tr_bencGetStr( tr_bencListChild( fields, i ), &str ) )
638            addField( tor, d, str );
639}
640
641static const char*
642torrentGet( tr_session               * session,
643            tr_benc                  * args_in,
644            tr_benc                  * args_out,
645            struct tr_rpc_idle_data  * idle_data UNUSED )
646{
647    int           i, torrentCount;
648    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
649    tr_benc *     list = tr_bencDictAddList( args_out, "torrents", torrentCount );
650    tr_benc *     fields;
651    const char *  msg = NULL;
652    const char *  strVal;
653
654    assert( idle_data == NULL );
655
656    if( tr_bencDictFindStr( args_in, "ids", &strVal ) && !strcmp( strVal, "recently-active" ) ) {
657        int n = 0;
658        tr_benc * d;
659        const time_t now = tr_time( );
660        const int interval = RECENTLY_ACTIVE_SECONDS;
661        tr_benc * removed_out = tr_bencDictAddList( args_out, "removed", 0 );
662        while(( d = tr_bencListChild( &session->removedTorrents, n++ ))) {
663            int64_t intVal;
664            if( tr_bencDictFindInt( d, "date", &intVal ) && ( intVal >= now - interval ) ) {
665                tr_bencDictFindInt( d, "id", &intVal );
666                tr_bencListAddInt( removed_out, intVal );
667            }
668        }
669    }
670
671    if( !tr_bencDictFindList( args_in, "fields", &fields ) )
672        msg = "no fields specified";
673    else for( i = 0; i < torrentCount; ++i )
674        addInfo( torrents[i], tr_bencListAdd( list ), fields );
675
676    tr_free( torrents );
677    return msg;
678}
679
680/***
681****
682***/
683
684static const char*
685setFilePriorities( tr_torrent * tor,
686                   int          priority,
687                   tr_benc *    list )
688{
689    int i;
690    int64_t tmp;
691    int fileCount = 0;
692    const int n = tr_bencListSize( list );
693    const char * errmsg = NULL;
694    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
695
696    if( n )
697    {
698        for( i = 0; i < n; ++i ) {
699            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) {
700                if( 0 <= tmp && tmp < tor->info.fileCount ) {
701                    files[fileCount++] = tmp;
702                } else {
703                    errmsg = "file index out of range";
704                }
705            }
706        }
707    }
708    else /* if empty set, apply to all */
709    {
710        tr_file_index_t t;
711        for( t = 0; t < tor->info.fileCount; ++t )
712            files[fileCount++] = t;
713    }
714
715    if( fileCount )
716        tr_torrentSetFilePriorities( tor, files, fileCount, priority );
717
718    tr_free( files );
719    return errmsg;
720}
721
722static const char*
723setFileDLs( tr_torrent * tor,
724            int          do_download,
725            tr_benc *    list )
726{
727    int i;
728    int64_t tmp;
729    int fileCount = 0;
730    const int n = tr_bencListSize( list );
731    const char * errmsg = NULL;
732    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
733
734    if( n ) /* if argument list, process them */
735    {
736        for( i = 0; i < n; ++i ) {
737            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) {
738                if( 0 <= tmp && tmp < tor->info.fileCount ) {
739                    files[fileCount++] = tmp;
740                } else {
741                    errmsg = "file index out of range";
742                }
743            }
744        }
745    }
746    else /* if empty set, apply to all */
747    {
748        tr_file_index_t t;
749        for( t = 0; t < tor->info.fileCount; ++t )
750            files[fileCount++] = t;
751    }
752
753    if( fileCount )
754        tr_torrentSetFileDLs( tor, files, fileCount, do_download );
755
756    tr_free( files );
757    return errmsg;
758}
759
760static tr_bool
761findAnnounceUrl( const tr_tracker_info * t, int n, const char * url, int * pos )
762{
763    int i;
764    tr_bool found = FALSE;
765
766    for( i=0; i<n; ++i )
767    {
768        if( !strcmp( t[i].announce, url ) )
769        {
770            found = TRUE;
771            if( pos ) *pos = i;
772            break;
773        }
774    }
775
776    return found;
777}
778
779static int
780copyTrackers( tr_tracker_info * tgt, const tr_tracker_info * src, int n )
781{
782    int i;
783    int maxTier = -1;
784   
785    for( i=0; i<n; ++i ) 
786    {
787        tgt[i].tier = src[i].tier;
788        tgt[i].announce = tr_strdup( src[i].announce );
789        maxTier = MAX( maxTier, src[i].tier );
790    }
791
792    return maxTier;
793}
794
795static void
796freeTrackers( tr_tracker_info * trackers, int n )
797{
798    int i;
799
800    for( i=0; i<n; ++i )
801        tr_free( trackers[i].announce );
802
803    tr_free( trackers );
804}
805
806static const char*
807addTrackerUrls( tr_torrent * tor, tr_benc * urls )
808{
809    int i;
810    int n;
811    int tier;
812    tr_benc * val;
813    tr_tracker_info * trackers;
814    tr_bool changed = FALSE;
815    const tr_info * inf = tr_torrentInfo( tor );
816    const char * errmsg = NULL;
817
818    /* make a working copy of the existing announce list */
819    n = inf->trackerCount;
820    trackers = tr_new0( tr_tracker_info, n + tr_bencListSize( urls ) );
821    tier = copyTrackers( trackers, inf->trackers, n );
822
823    /* and add the new ones */
824    i = 0;
825    while(( val = tr_bencListChild( urls, i++ ) ))
826    {
827        const char * announce = NULL;
828
829        if(    tr_bencGetStr( val, &announce )
830            && tr_urlIsValid( announce, -1 )
831            && !findAnnounceUrl( trackers, n, announce, NULL ) )
832        {
833            trackers[n].tier = ++tier; /* add a new tier */
834            trackers[n].announce = tr_strdup( announce );
835            ++n;
836            changed = TRUE;
837        }
838    }
839
840    if( !changed )
841        errmsg = "invalid argument";
842    else if( !tr_torrentSetAnnounceList( tor, trackers, n ) )
843        errmsg = "error setting announce list";
844
845    freeTrackers( trackers, n );
846    return errmsg;
847}
848
849static const char*
850replaceTrackers( tr_torrent * tor, tr_benc * urls )
851{
852    int i;
853    tr_benc * pair[2];
854    tr_tracker_info * trackers;
855    tr_bool changed = FALSE;
856    const tr_info * inf = tr_torrentInfo( tor );
857    const int n = inf->trackerCount;
858    const char * errmsg = NULL;
859
860    /* make a working copy of the existing announce list */
861    trackers = tr_new0( tr_tracker_info, n );
862    copyTrackers( trackers, inf->trackers, n );
863
864    /* make the substitutions... */
865    i = 0;
866    while(((pair[0] = tr_bencListChild(urls,i))) &&
867          ((pair[1] = tr_bencListChild(urls,i+1))))
868    {
869        int64_t pos;
870        const char * newval;
871
872        if(    tr_bencGetInt( pair[0], &pos )
873            && tr_bencGetStr( pair[1], &newval )
874            && tr_urlIsValid( newval, -1 )
875            && pos < n )
876        {
877            tr_free( trackers[pos].announce );
878            trackers[pos].announce = tr_strdup( newval );
879            changed = TRUE;
880        }
881
882        i += 2;
883    }
884
885    if( !changed )
886        errmsg = "invalid argument";
887    else if( !tr_torrentSetAnnounceList( tor, trackers, n ) )
888        errmsg = "error setting announce list";
889
890    freeTrackers( trackers, n );
891    return errmsg;
892}
893
894static const char*
895removeTrackers( tr_torrent * tor, tr_benc * ids )
896{
897    int i;
898    int n;
899    int * tids;
900    int t = 0;
901    tr_benc * val;
902    tr_tracker_info * trackers;
903    tr_bool changed = FALSE;
904    const tr_info * inf = tr_torrentInfo( tor );
905    const char * errmsg = NULL;
906
907    /* make a working copy of the existing announce list */
908    n = inf->trackerCount;
909    tids = tr_new0( int, n );
910    trackers = tr_new0( tr_tracker_info, n );
911    copyTrackers( trackers, inf->trackers, n );
912
913    /* remove the ones specified in the urls list */
914    i = 0;
915    while(( val = tr_bencListChild( ids, i++ )))
916    {
917        int64_t pos;
918        if( tr_bencGetInt( val, &pos ) && pos < n )
919            tids[t++] = pos;
920    }
921
922    /* sort trackerIds because tr_removeElementFromArray changes indices as it removes */
923    qsort( tids, t, sizeof(int), compareInt );
924
925    /* remove from largest trackerId to smallest */
926    while( t-- )
927    {
928        tr_removeElementFromArray( trackers, tids[t], sizeof( tr_tracker_info ), n-- );
929        changed = TRUE;
930    }
931
932    if( !changed )
933        errmsg = "invalid argument";
934    else if( !tr_torrentSetAnnounceList( tor, trackers, n ) )
935        errmsg = "error setting announce list";
936
937    freeTrackers( trackers, n );
938    tr_free( tids );
939    return errmsg;
940}
941
942static const char*
943torrentSet( tr_session               * session,
944            tr_benc                  * args_in,
945            tr_benc                  * args_out UNUSED,
946            struct tr_rpc_idle_data  * idle_data UNUSED )
947{
948    const char * errmsg = NULL;
949    int i, torrentCount;
950    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
951
952    assert( idle_data == NULL );
953
954    for( i = 0; i < torrentCount; ++i )
955    {
956        int64_t      tmp;
957        double       d;
958        tr_benc *    files;
959        tr_benc *    trackers;
960        tr_bool      boolVal;
961        tr_torrent * tor = torrents[i];
962
963        if( tr_bencDictFindInt( args_in, "bandwidthPriority", &tmp ) )
964            if( tr_isPriority( tmp ) )
965                tr_torrentSetPriority( tor, tmp );
966        if( !errmsg && tr_bencDictFindList( args_in, "files-unwanted", &files ) )
967            errmsg = setFileDLs( tor, FALSE, files );
968        if( !errmsg && tr_bencDictFindList( args_in, "files-wanted", &files ) )
969            errmsg = setFileDLs( tor, TRUE, files );
970        if( tr_bencDictFindInt( args_in, "peer-limit", &tmp ) )
971            tr_torrentSetPeerLimit( tor, tmp );
972        if( !errmsg &&  tr_bencDictFindList( args_in, "priority-high", &files ) )
973            errmsg = setFilePriorities( tor, TR_PRI_HIGH, files );
974        if( !errmsg && tr_bencDictFindList( args_in, "priority-low", &files ) )
975            errmsg = setFilePriorities( tor, TR_PRI_LOW, files );
976        if( !errmsg && tr_bencDictFindList( args_in, "priority-normal", &files ) )
977            errmsg = setFilePriorities( tor, TR_PRI_NORMAL, files );
978        if( tr_bencDictFindInt( args_in, "downloadLimit", &tmp ) )
979            tr_torrentSetSpeedLimit_KBps( tor, TR_DOWN, tmp );
980        if( tr_bencDictFindBool( args_in, "downloadLimited", &boolVal ) )
981            tr_torrentUseSpeedLimit( tor, TR_DOWN, boolVal );
982        if( tr_bencDictFindBool( args_in, "honorsSessionLimits", &boolVal ) )
983            tr_torrentUseSessionLimits( tor, boolVal );
984        if( tr_bencDictFindInt( args_in, "uploadLimit", &tmp ) )
985            tr_torrentSetSpeedLimit_KBps( tor, TR_UP, tmp );
986        if( tr_bencDictFindBool( args_in, "uploadLimited", &boolVal ) )
987            tr_torrentUseSpeedLimit( tor, TR_UP, boolVal );
988        if( tr_bencDictFindInt( args_in, "seedIdleLimit", &tmp ) )
989            tr_torrentSetIdleLimit( tor, tmp );
990        if( tr_bencDictFindInt( args_in, "seedIdleMode", &tmp ) )
991            tr_torrentSetIdleMode( tor, tmp );
992        if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
993            tr_torrentSetRatioLimit( tor, d );
994        if( tr_bencDictFindInt( args_in, "seedRatioMode", &tmp ) )
995            tr_torrentSetRatioMode( tor, tmp );
996        if( !errmsg && tr_bencDictFindList( args_in, "trackerAdd", &trackers ) )
997            errmsg = addTrackerUrls( tor, trackers );
998        if( !errmsg && tr_bencDictFindList( args_in, "trackerRemove", &trackers ) )
999            errmsg = removeTrackers( tor, trackers );
1000        if( !errmsg && tr_bencDictFindList( args_in, "trackerReplace", &trackers ) )
1001            errmsg = replaceTrackers( tor, trackers );
1002        notify( session, TR_RPC_TORRENT_CHANGED, tor );
1003    }
1004
1005    tr_free( torrents );
1006    return errmsg;
1007}
1008
1009static const char*
1010torrentSetLocation( tr_session               * session,
1011                    tr_benc                  * args_in,
1012                    tr_benc                  * args_out UNUSED,
1013                    struct tr_rpc_idle_data  * idle_data UNUSED )
1014{
1015    const char * errmsg = NULL;
1016    const char * location = NULL;
1017
1018    assert( idle_data == NULL );
1019
1020    if( !tr_bencDictFindStr( args_in, "location", &location ) )
1021    {
1022        errmsg = "no location";
1023    }
1024    else
1025    {
1026        tr_bool move = FALSE;
1027        int i, torrentCount;
1028        tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
1029
1030        tr_bencDictFindBool( args_in, "move", &move );
1031
1032        for( i=0; i<torrentCount; ++i )
1033        {
1034            tr_torrent * tor = torrents[i];
1035            tr_torrentSetLocation( tor, location, move, NULL, NULL );
1036            notify( session, TR_RPC_TORRENT_MOVED, tor );
1037        }
1038
1039        tr_free( torrents );
1040    }
1041
1042    return errmsg;
1043}
1044
1045/***
1046****
1047***/
1048
1049static void
1050portTested( tr_session       * session UNUSED,
1051            long               response_code,
1052            const void       * response,
1053            size_t             response_byte_count,
1054            void             * user_data )
1055{
1056    char result[1024];
1057    struct tr_rpc_idle_data * data = user_data;
1058
1059    if( response_code != 200 )
1060    {
1061        tr_snprintf( result, sizeof( result ), "portTested: http error %ld: %s",
1062                     response_code, tr_webGetResponseStr( response_code ) );
1063    }
1064    else /* success */
1065    {
1066        const tr_bool isOpen = response_byte_count && *(char*)response == '1';
1067        tr_bencDictAddBool( data->args_out, "port-is-open", isOpen );
1068        tr_snprintf( result, sizeof( result ), "success" );
1069    }
1070
1071    tr_idle_function_done( data, result );
1072}
1073
1074static const char*
1075portTest( tr_session               * session,
1076          tr_benc                  * args_in UNUSED,
1077          tr_benc                  * args_out UNUSED,
1078          struct tr_rpc_idle_data  * idle_data )
1079{
1080    const int port = tr_sessionGetPeerPort( session );
1081    char * url = tr_strdup_printf( "http://portcheck.transmissionbt.com/%d", port );
1082    tr_webRun( session, url, NULL, portTested, idle_data );
1083    tr_free( url );
1084    return NULL;
1085}
1086
1087/***
1088****
1089***/
1090
1091static void
1092gotNewBlocklist( tr_session       * session,
1093                 long               response_code,
1094                 const void       * response,
1095                 size_t             response_byte_count,
1096                 void             * user_data )
1097{
1098    char result[1024];
1099    struct tr_rpc_idle_data * data = user_data;
1100
1101    if( response_code != 200 )
1102    {
1103        tr_snprintf( result, sizeof( result ), "gotNewBlocklist: http error %ld: %s",
1104                     response_code, tr_webGetResponseStr( response_code ) );
1105    }
1106    else /* successfully fetched the blocklist... */
1107    {
1108        const char * configDir = tr_sessionGetConfigDir( session );
1109        char * filename = tr_buildPath( configDir, "blocklist.tmp", NULL );
1110        FILE * fp = fopen( filename, "wb+" );
1111
1112        if( fp == NULL )
1113        {
1114            tr_snprintf( result, sizeof( result ),
1115                         _( "Couldn't save file \"%1$s\": %2$s" ),
1116                         filename, tr_strerror( errno ) );
1117        }
1118        else
1119        {
1120            const size_t n = fwrite( response, 1, response_byte_count, fp );
1121            fclose( fp );
1122
1123            if( n != response_byte_count )
1124            {
1125                tr_snprintf( result, sizeof( result ),
1126                             _( "Couldn't save file \"%1$s\": %2$s" ),
1127                             filename, tr_strerror( errno ) );
1128            }
1129            else
1130            {
1131                /* feed it to the session */
1132                const int ruleCount = tr_blocklistSetContent( session, filename );
1133
1134                /* give the client a response */
1135                tr_bencDictAddInt( data->args_out, "blocklist-size", ruleCount );
1136                tr_snprintf( result, sizeof( result ), "success" );
1137            }
1138
1139            /* cleanup */
1140            unlink( filename );
1141        }
1142
1143        /* cleanup */
1144        tr_free( filename );
1145    }
1146
1147    tr_idle_function_done( data, result );
1148}
1149
1150static const char*
1151blocklistUpdate( tr_session               * session,
1152                 tr_benc                  * args_in UNUSED,
1153                 tr_benc                  * args_out UNUSED,
1154                 struct tr_rpc_idle_data  * idle_data )
1155{
1156    const char * url = "http://update.transmissionbt.com/level1";
1157    tr_webRun( session, url, NULL, gotNewBlocklist, idle_data );
1158    return NULL;
1159}
1160
1161/***
1162****
1163***/
1164
1165static void
1166addTorrentImpl( struct tr_rpc_idle_data * data, tr_ctor * ctor )
1167{
1168    int err = 0;
1169    const char * result = NULL;
1170    tr_torrent * tor = tr_torrentNew( ctor, &err );
1171
1172    tr_ctorFree( ctor );
1173
1174    if( tor )
1175    {
1176        tr_benc fields;
1177        tr_bencInitList( &fields, 3 );
1178        tr_bencListAddStr( &fields, "id" );
1179        tr_bencListAddStr( &fields, "name" );
1180        tr_bencListAddStr( &fields, "hashString" );
1181        addInfo( tor, tr_bencDictAdd( data->args_out, "torrent-added" ), &fields );
1182        notify( data->session, TR_RPC_TORRENT_ADDED, tor );
1183        tr_bencFree( &fields );
1184    }
1185    else if( err == TR_PARSE_DUPLICATE )
1186    {
1187        result = "duplicate torrent";
1188    }
1189    else if( err == TR_PARSE_ERR )
1190    {
1191        result = "invalid or corrupt torrent file";
1192    }
1193
1194    tr_idle_function_done( data, result );
1195}
1196
1197
1198struct add_torrent_idle_data
1199{
1200    struct tr_rpc_idle_data * data;
1201    tr_ctor * ctor;
1202};
1203
1204static void
1205gotMetadataFromURL( tr_session       * session UNUSED,
1206                    long               response_code,
1207                    const void       * response,
1208                    size_t             response_byte_count,
1209                    void             * user_data )
1210{
1211    struct add_torrent_idle_data * data = user_data;
1212
1213    dbgmsg( "torrentAdd: HTTP response code was %ld (%s); response length was %zu bytes",
1214            response_code, tr_webGetResponseStr( response_code ), response_byte_count );
1215
1216    if( response_code==200 || response_code==221 ) /* http or ftp success.. */
1217    {
1218        tr_ctorSetMetainfo( data->ctor, response, response_byte_count );
1219        addTorrentImpl( data->data, data->ctor );
1220    }
1221    else
1222    {
1223        char result[1024];
1224        tr_snprintf( result, sizeof( result ), "gotMetadataFromURL: http error %ld: %s",
1225                     response_code, tr_webGetResponseStr( response_code ) );
1226        tr_idle_function_done( data->data, result );
1227    }
1228
1229    tr_free( data );
1230}
1231
1232static tr_bool
1233isCurlURL( const char * filename )
1234{
1235    if( filename == NULL )
1236        return FALSE;
1237
1238    return !strncmp( filename, "ftp://", 6 ) ||
1239           !strncmp( filename, "http://", 7 ) ||
1240           !strncmp( filename, "https://", 8 );
1241}
1242
1243static tr_file_index_t*
1244fileListFromList( tr_benc * list, tr_file_index_t * setmeCount )
1245{
1246    size_t i;
1247    const size_t childCount = tr_bencListSize( list );
1248    tr_file_index_t n = 0;
1249    tr_file_index_t * files = tr_new0( tr_file_index_t, childCount );
1250
1251    for( i=0; i<childCount; ++i ) {
1252        int64_t intVal;
1253        if( tr_bencGetInt( tr_bencListChild( list, i ), &intVal ) )
1254            files[n++] = (tr_file_index_t)intVal;
1255    }
1256
1257    *setmeCount = n;
1258    return files;
1259}
1260
1261static const char*
1262torrentAdd( tr_session               * session,
1263            tr_benc                  * args_in,
1264            tr_benc                  * args_out UNUSED,
1265            struct tr_rpc_idle_data  * idle_data )
1266{
1267    const char * filename = NULL;
1268    const char * metainfo_base64 = NULL;
1269
1270    assert( idle_data != NULL );
1271
1272    tr_bencDictFindStr( args_in, "filename", &filename );
1273    tr_bencDictFindStr( args_in, "metainfo", &metainfo_base64 );
1274    if( !filename && !metainfo_base64 )
1275        return "no filename or metainfo specified";
1276    else
1277    {
1278        int64_t      i;
1279        tr_bool      boolVal;
1280        const char * str;
1281        tr_benc    * l;
1282        tr_ctor    * ctor = tr_ctorNew( session );
1283
1284        /* set the optional arguments */
1285
1286        if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1287            tr_ctorSetDownloadDir( ctor, TR_FORCE, str );
1288
1289        if( tr_bencDictFindBool( args_in, "paused", &boolVal ) )
1290            tr_ctorSetPaused( ctor, TR_FORCE, boolVal );
1291
1292        if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
1293            tr_ctorSetPeerLimit( ctor, TR_FORCE, i );
1294
1295        if( tr_bencDictFindInt( args_in, "bandwidthPriority", &i ) )
1296            tr_ctorSetBandwidthPriority( ctor, i );
1297
1298        if( tr_bencDictFindList( args_in, "files-unwanted", &l ) ) {
1299            tr_file_index_t fileCount;
1300            tr_file_index_t * files = fileListFromList( l, &fileCount );
1301            tr_ctorSetFilesWanted( ctor, files, fileCount, FALSE );
1302            tr_free( files );
1303        }
1304        if( tr_bencDictFindList( args_in, "files-wanted", &l ) ) {
1305            tr_file_index_t fileCount;
1306            tr_file_index_t * files = fileListFromList( l, &fileCount );
1307            tr_ctorSetFilesWanted( ctor, files, fileCount, TRUE );
1308            tr_free( files );
1309        }
1310
1311        if( tr_bencDictFindList( args_in, "priority-low", &l ) ) {
1312            tr_file_index_t fileCount;
1313            tr_file_index_t * files = fileListFromList( l, &fileCount );
1314            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_LOW );
1315            tr_free( files );
1316        }
1317        if( tr_bencDictFindList( args_in, "priority-normal", &l ) ) {
1318            tr_file_index_t fileCount;
1319            tr_file_index_t * files = fileListFromList( l, &fileCount );
1320            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_NORMAL );
1321            tr_free( files );
1322        }
1323        if( tr_bencDictFindList( args_in, "priority-high", &l ) ) {
1324            tr_file_index_t fileCount;
1325            tr_file_index_t * files = fileListFromList( l, &fileCount );
1326            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_HIGH );
1327            tr_free( files );
1328        }
1329
1330        dbgmsg( "torrentAdd: filename is \"%s\"", filename ? filename : "(null)" );
1331
1332        if( isCurlURL( filename ) )
1333        {
1334            struct add_torrent_idle_data * d = tr_new0( struct add_torrent_idle_data, 1 );
1335            d->data = idle_data;
1336            d->ctor = ctor;
1337            tr_webRun( session, filename, NULL, gotMetadataFromURL, d );
1338        }
1339        else
1340        {
1341            char * fname = tr_strstrip( tr_strdup( filename ) );
1342
1343            if( fname == NULL )
1344            {
1345                int len;
1346                char * metainfo = tr_base64_decode( metainfo_base64, -1, &len );
1347                tr_ctorSetMetainfo( ctor, (uint8_t*)metainfo, len );
1348                tr_free( metainfo );
1349            }
1350            else if( !strncmp( fname, "magnet:?", 8 ) )
1351            {
1352                tr_ctorSetMetainfoFromMagnetLink( ctor, fname );
1353            }
1354            else
1355            {
1356                tr_ctorSetMetainfoFromFile( ctor, fname );
1357            }
1358
1359            addTorrentImpl( idle_data, ctor );
1360
1361            tr_free( fname );
1362        }
1363
1364    }
1365
1366    return NULL;
1367}
1368
1369/***
1370****
1371***/
1372
1373static const char*
1374sessionSet( tr_session               * session,
1375            tr_benc                  * args_in,
1376            tr_benc                  * args_out UNUSED,
1377            struct tr_rpc_idle_data  * idle_data UNUSED )
1378{
1379    int64_t      i;
1380    double       d;
1381    tr_bool      boolVal;
1382    const char * str;
1383
1384    assert( idle_data == NULL );
1385
1386    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, &i ) )
1387        tr_sessionSetCacheLimit_MB( session, i );
1388    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_UP_KBps, &i ) )
1389        tr_sessionSetAltSpeed_KBps( session, TR_UP, i );
1390    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, &i ) )
1391        tr_sessionSetAltSpeed_KBps( session, TR_DOWN, i );
1392    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_ENABLED, &boolVal ) )
1393        tr_sessionUseAltSpeed( session, boolVal );
1394    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &i ) )
1395        tr_sessionSetAltSpeedBegin( session, i );
1396    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_END, &i ) )
1397        tr_sessionSetAltSpeedEnd( session, i );
1398    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &i ) )
1399        tr_sessionSetAltSpeedDay( session, i );
1400    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &boolVal ) )
1401        tr_sessionUseAltSpeedTime( session, boolVal );
1402    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_BLOCKLIST_ENABLED, &boolVal ) )
1403        tr_blocklistSetEnabled( session, boolVal );
1404    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1405        tr_sessionSetDownloadDir( session, str );
1406    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_INCOMPLETE_DIR, &str ) )
1407        tr_sessionSetIncompleteDir( session, str );
1408    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, &boolVal ) )
1409        tr_sessionSetIncompleteDirEnabled( session, boolVal );
1410    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &i ) )
1411        tr_sessionSetPeerLimit( session, i );
1412    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i ) )
1413        tr_sessionSetPeerLimitPerTorrent( session, i );
1414    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) )
1415        tr_sessionSetPexEnabled( session, boolVal );
1416    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DHT_ENABLED, &boolVal ) )
1417        tr_sessionSetDHTEnabled( session, boolVal );
1418    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_LPD_ENABLED, &boolVal ) )
1419        tr_sessionSetLPDEnabled( session, boolVal );
1420    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, &boolVal ) )
1421        tr_sessionSetPeerPortRandomOnStart( session, boolVal );
1422    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_PORT, &i ) )
1423        tr_sessionSetPeerPort( session, i );
1424    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) )
1425        tr_sessionSetPortForwardingEnabled( session, boolVal );
1426    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_RENAME_PARTIAL_FILES, &boolVal ) )
1427        tr_sessionSetIncompleteFileNamingEnabled( session, boolVal );
1428    if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
1429        tr_sessionSetRatioLimit( session, d );
1430    if( tr_bencDictFindBool( args_in, "seedRatioLimited", &boolVal ) )
1431        tr_sessionSetRatioLimited( session, boolVal );
1432    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_IDLE_LIMIT, &i ) )
1433        tr_sessionSetIdleLimit( session, i );
1434    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, &boolVal ) )
1435        tr_sessionSetIdleLimited( session, boolVal );
1436    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_START, &boolVal ) )
1437        tr_sessionSetPaused( session, !boolVal );
1438    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, &str ) )
1439        tr_sessionSetTorrentDoneScript( session, str );
1440    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, &boolVal ) )
1441        tr_sessionSetTorrentDoneScriptEnabled( session, boolVal );
1442    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_TRASH_ORIGINAL, &boolVal ) )
1443        tr_sessionSetDeleteSource( session, boolVal );
1444    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_DSPEED_KBps, &i ) )
1445        tr_sessionSetSpeedLimit_KBps( session, TR_DOWN, i );
1446    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DSPEED_ENABLED, &boolVal ) )
1447        tr_sessionLimitSpeed( session, TR_DOWN, boolVal );
1448    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_USPEED_KBps, &i ) )
1449        tr_sessionSetSpeedLimit_KBps( session, TR_UP, i );
1450    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_USPEED_ENABLED, &boolVal ) )
1451        tr_sessionLimitSpeed( session, TR_UP, boolVal );
1452    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_ENCRYPTION, &str ) ) {
1453        if( !strcmp( str, "required" ) )
1454            tr_sessionSetEncryption( session, TR_ENCRYPTION_REQUIRED );
1455        else if( !strcmp( str, "tolerated" ) )
1456            tr_sessionSetEncryption( session, TR_CLEAR_PREFERRED );
1457        else
1458            tr_sessionSetEncryption( session, TR_ENCRYPTION_PREFERRED );
1459    }
1460
1461    notify( session, TR_RPC_SESSION_CHANGED, NULL );
1462
1463    return NULL;
1464}
1465
1466static const char*
1467sessionStats( tr_session               * session,
1468              tr_benc                  * args_in UNUSED,
1469              tr_benc                  * args_out,
1470              struct tr_rpc_idle_data  * idle_data UNUSED )
1471{
1472    int running = 0;
1473    int total = 0;
1474    tr_benc * d;
1475    tr_session_stats currentStats = { 0.0f, 0, 0, 0, 0, 0 };
1476    tr_session_stats cumulativeStats = { 0.0f, 0, 0, 0, 0, 0 };
1477    tr_torrent * tor = NULL;
1478
1479    assert( idle_data == NULL );
1480
1481    while(( tor = tr_torrentNext( session, tor ))) {
1482        ++total;
1483        if( tor->isRunning )
1484            ++running;
1485    }
1486
1487    tr_sessionGetStats( session, &currentStats );
1488    tr_sessionGetCumulativeStats( session, &cumulativeStats );
1489
1490    tr_bencDictAddInt ( args_out, "activeTorrentCount", running );
1491    tr_bencDictAddReal( args_out, "downloadSpeed", tr_sessionGetPieceSpeed_KBps( session, TR_DOWN ) );
1492    tr_bencDictAddInt ( args_out, "pausedTorrentCount", total - running );
1493    tr_bencDictAddInt ( args_out, "torrentCount", total );
1494    tr_bencDictAddReal( args_out, "uploadSpeed", tr_sessionGetPieceSpeed_KBps( session, TR_UP ) );
1495
1496    d = tr_bencDictAddDict( args_out, "cumulative-stats", 5 );
1497    tr_bencDictAddInt( d, "downloadedBytes", cumulativeStats.downloadedBytes );
1498    tr_bencDictAddInt( d, "filesAdded", cumulativeStats.filesAdded );
1499    tr_bencDictAddInt( d, "secondsActive", cumulativeStats.secondsActive );
1500    tr_bencDictAddInt( d, "sessionCount", cumulativeStats.sessionCount );
1501    tr_bencDictAddInt( d, "uploadedBytes", cumulativeStats.uploadedBytes );
1502
1503    d = tr_bencDictAddDict( args_out, "current-stats", 5 );
1504    tr_bencDictAddInt( d, "downloadedBytes", currentStats.downloadedBytes );
1505    tr_bencDictAddInt( d, "filesAdded", currentStats.filesAdded );
1506    tr_bencDictAddInt( d, "secondsActive", currentStats.secondsActive );
1507    tr_bencDictAddInt( d, "sessionCount", currentStats.sessionCount );
1508    tr_bencDictAddInt( d, "uploadedBytes", currentStats.uploadedBytes );
1509
1510    return NULL;
1511}
1512
1513static const char*
1514sessionGet( tr_session               * s,
1515            tr_benc                  * args_in UNUSED,
1516            tr_benc                  * args_out,
1517            struct tr_rpc_idle_data  * idle_data UNUSED )
1518{
1519    const char * str;
1520    tr_benc *    d = args_out;
1521
1522    assert( idle_data == NULL );
1523    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP_KBps, tr_sessionGetAltSpeed_KBps(s,TR_UP) );
1524    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, tr_sessionGetAltSpeed_KBps(s,TR_DOWN) );
1525    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED, tr_sessionUsesAltSpeed(s) );
1526    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, tr_sessionGetAltSpeedBegin(s) );
1527    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,tr_sessionGetAltSpeedEnd(s) );
1528    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,tr_sessionGetAltSpeedDay(s) );
1529    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, tr_sessionUsesAltSpeedTime(s) );
1530    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, tr_blocklistIsEnabled( s ) );
1531    tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, tr_sessionGetCacheLimit_MB( s ) );
1532    tr_bencDictAddInt ( d, "blocklist-size", tr_blocklistGetRuleCount( s ) );
1533    tr_bencDictAddStr ( d, "config-dir", tr_sessionGetConfigDir( s ) );
1534    tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR, tr_sessionGetDownloadDir( s ) );
1535    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, tr_sessionGetPeerLimit( s ) );
1536    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT, tr_sessionGetPeerLimitPerTorrent( s ) );
1537    tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR, tr_sessionGetIncompleteDir( s ) );
1538    tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, tr_sessionIsIncompleteDirEnabled( s ) );
1539    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED, tr_sessionIsPexEnabled( s ) );
1540    tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED, tr_sessionIsDHTEnabled( s ) );
1541    tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED, tr_sessionIsLPDEnabled( s ) );
1542    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT, tr_sessionGetPeerPort( s ) );
1543    tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, tr_sessionGetPeerPortRandomOnStart( s ) );
1544    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) );
1545    tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES, tr_sessionIsIncompleteFileNamingEnabled( s ) );
1546    tr_bencDictAddInt ( d, "rpc-version", RPC_VERSION );
1547    tr_bencDictAddInt ( d, "rpc-version-minimum", RPC_VERSION_MIN );
1548    tr_bencDictAddReal( d, "seedRatioLimit", tr_sessionGetRatioLimit( s ) );
1549    tr_bencDictAddBool( d, "seedRatioLimited", tr_sessionIsRatioLimited( s ) );
1550    tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT, tr_sessionGetIdleLimit( s ) );
1551    tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, tr_sessionIsIdleLimited( s ) );
1552    tr_bencDictAddBool( d, TR_PREFS_KEY_START, !tr_sessionGetPaused( s ) );
1553    tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL, tr_sessionGetDeleteSource( s ) );
1554    tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_UP ) );
1555    tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_UP ) );
1556    tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_DOWN ) );
1557    tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_DOWN ) );
1558    tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, tr_sessionGetTorrentDoneScript( s ) );
1559    tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, tr_sessionIsTorrentDoneScriptEnabled( s ) );
1560    tr_bencDictAddStr ( d, "version", LONG_VERSION_STRING );
1561    switch( tr_sessionGetEncryption( s ) ) {
1562        case TR_CLEAR_PREFERRED: str = "tolerated"; break;
1563        case TR_ENCRYPTION_REQUIRED: str = "required"; break;
1564        default: str = "preferred"; break;
1565    }
1566    tr_bencDictAddStr( d, TR_PREFS_KEY_ENCRYPTION, str );
1567
1568    return NULL;
1569}
1570
1571/***
1572****
1573***/
1574
1575typedef const char* ( *handler )( tr_session*, tr_benc*, tr_benc*, struct tr_rpc_idle_data * );
1576
1577static struct method
1578{
1579    const char *  name;
1580    tr_bool       immediate;
1581    handler       func;
1582}
1583methods[] =
1584{
1585    { "port-test",             FALSE, portTest            },
1586    { "blocklist-update",      FALSE, blocklistUpdate     },
1587    { "session-get",           TRUE,  sessionGet          },
1588    { "session-set",           TRUE,  sessionSet          },
1589    { "session-stats",         TRUE,  sessionStats        },
1590    { "torrent-add",           FALSE, torrentAdd          },
1591    { "torrent-get",           TRUE,  torrentGet          },
1592    { "torrent-remove",        TRUE,  torrentRemove       },
1593    { "torrent-set",           TRUE,  torrentSet          },
1594    { "torrent-set-location",  TRUE,  torrentSetLocation  },
1595    { "torrent-start",         TRUE,  torrentStart        },
1596    { "torrent-stop",          TRUE,  torrentStop         },
1597    { "torrent-verify",        TRUE,  torrentVerify       },
1598    { "torrent-reannounce",    TRUE,  torrentReannounce   }
1599};
1600
1601static void
1602noop_response_callback( tr_session * session UNUSED,
1603                        const char * response UNUSED,
1604                        size_t       response_len UNUSED,
1605                        void       * user_data UNUSED )
1606{
1607}
1608
1609static void
1610request_exec( tr_session             * session,
1611              tr_benc                * request,
1612              tr_rpc_response_func     callback,
1613              void                   * callback_user_data )
1614{
1615    int i;
1616    const char * str;
1617    tr_benc * args_in = tr_bencDictFind( request, "arguments" );
1618    const char * result = NULL;
1619
1620    if( callback == NULL )
1621        callback = noop_response_callback;
1622
1623    /* parse the request */
1624    if( !tr_bencDictFindStr( request, "method", &str ) )
1625        result = "no method name";
1626    else {
1627        const int n = TR_N_ELEMENTS( methods );
1628        for( i = 0; i < n; ++i )
1629            if( !strcmp( str, methods[i].name ) )
1630                break;
1631        if( i ==n )
1632            result = "method name not recognized";
1633    }
1634
1635    /* if we couldn't figure out which method to use, return an error */
1636    if( result != NULL )
1637    {
1638        int64_t tag;
1639        tr_benc response;
1640        struct evbuffer * buf = evbuffer_new( );
1641
1642        tr_bencInitDict( &response, 3 );
1643        tr_bencDictAddDict( &response, "arguments", 0 );
1644        tr_bencDictAddStr( &response, "result", result );
1645        if( tr_bencDictFindInt( request, "tag", &tag ) )
1646            tr_bencDictAddInt( &response, "tag", tag );
1647        tr_bencToBuf( &response, TR_FMT_JSON_LEAN, buf );
1648        (*callback)( session, (const char*)EVBUFFER_DATA(buf),
1649                     EVBUFFER_LENGTH( buf ), callback_user_data );
1650
1651        evbuffer_free( buf );
1652        tr_bencFree( &response );
1653    }
1654    else if( methods[i].immediate )
1655    {
1656        int64_t tag;
1657        tr_benc response;
1658        tr_benc * args_out;
1659        struct evbuffer * buf = evbuffer_new( );
1660
1661        tr_bencInitDict( &response, 3 );
1662        args_out = tr_bencDictAddDict( &response, "arguments", 0 );
1663        result = (*methods[i].func)( session, args_in, args_out, NULL );
1664        if( result == NULL )
1665            result = "success";
1666        tr_bencDictAddStr( &response, "result", result );
1667        if( tr_bencDictFindInt( request, "tag", &tag ) )
1668            tr_bencDictAddInt( &response, "tag", tag );
1669        tr_bencToBuf( &response, TR_FMT_JSON_LEAN, buf );
1670        (*callback)( session, (const char*)EVBUFFER_DATA(buf),
1671                     EVBUFFER_LENGTH(buf), callback_user_data );
1672
1673        evbuffer_free( buf );
1674        tr_bencFree( &response );
1675    }
1676    else
1677    {
1678        int64_t tag;
1679        struct tr_rpc_idle_data * data = tr_new0( struct tr_rpc_idle_data, 1 );
1680        data->session = session;
1681        data->response = tr_new0( tr_benc, 1 );
1682        tr_bencInitDict( data->response, 3 );
1683        if( tr_bencDictFindInt( request, "tag", &tag ) )
1684            tr_bencDictAddInt( data->response, "tag", tag );
1685        data->args_out = tr_bencDictAddDict( data->response, "arguments", 0 );
1686        data->callback = callback;
1687        data->callback_user_data = callback_user_data;
1688        (*methods[i].func)( session, args_in, data->args_out, data );
1689    }
1690}
1691
1692void
1693tr_rpc_request_exec_json( tr_session            * session,
1694                          const void            * request_json,
1695                          int                     request_len,
1696                          tr_rpc_response_func    callback,
1697                          void                  * callback_user_data )
1698{
1699    tr_benc top;
1700    int have_content;
1701
1702    if( request_len < 0 )
1703        request_len = strlen( request_json );
1704
1705    have_content = !tr_jsonParse( "rpc", request_json, request_len, &top, NULL );
1706    request_exec( session, have_content ? &top : NULL, callback, callback_user_data );
1707
1708    if( have_content )
1709        tr_bencFree( &top );
1710}
1711
1712/**
1713 * Munge the URI into a usable form.
1714 *
1715 * We have very loose typing on this to make the URIs as simple as possible:
1716 * - anything not a 'tag' or 'method' is automatically in 'arguments'
1717 * - values that are all-digits are numbers
1718 * - values that are all-digits or commas are number lists
1719 * - all other values are strings
1720 */
1721void
1722tr_rpc_parse_list_str( tr_benc     * setme,
1723                       const char  * str,
1724                       int           len )
1725
1726{
1727    int valueCount;
1728    int * values = tr_parseNumberRange( str, len, &valueCount );
1729
1730    if( valueCount == 0 )
1731        tr_bencInitStr( setme, str, len );
1732    else if( valueCount == 1 )
1733        tr_bencInitInt( setme, values[0] );
1734    else {
1735        int i;
1736        tr_bencInitList( setme, valueCount );
1737        for( i=0; i<valueCount; ++i )
1738            tr_bencListAddInt( setme, values[i] );
1739    }
1740
1741    tr_free( values );
1742}
1743
1744void
1745tr_rpc_request_exec_uri( tr_session           * session,
1746                         const void           * request_uri,
1747                         int                    request_len,
1748                         tr_rpc_response_func   callback,
1749                         void                 * callback_user_data )
1750{
1751    tr_benc      top, * args;
1752    char *       request = tr_strndup( request_uri, request_len );
1753    const char * pch;
1754
1755    tr_bencInitDict( &top, 3 );
1756    args = tr_bencDictAddDict( &top, "arguments", 0 );
1757
1758    pch = strchr( request, '?' );
1759    if( !pch ) pch = request;
1760    while( pch )
1761    {
1762        const char * delim = strchr( pch, '=' );
1763        const char * next = strchr( pch, '&' );
1764        if( delim )
1765        {
1766            char *    key = tr_strndup( pch, delim - pch );
1767            int       isArg = strcmp( key, "method" ) && strcmp( key, "tag" );
1768            tr_benc * parent = isArg ? args : &top;
1769            tr_rpc_parse_list_str( tr_bencDictAdd( parent, key ),
1770                                  delim + 1,
1771                                  next ? (size_t)(
1772                                       next -
1773                                      ( delim + 1 ) ) : strlen( delim + 1 ) );
1774            tr_free( key );
1775        }
1776        pch = next ? next + 1 : NULL;
1777    }
1778
1779    request_exec( session, &top, callback, callback_user_data );
1780
1781    /* cleanup */
1782    tr_bencFree( &top );
1783    tr_free( request );
1784}
Note: See TracBrowser for help on using the repository browser.