source: trunk/libtransmission/rpcimpl.c @ 11212

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

(trunk) revert downloadSpeed and uploadSpeed in session stats to Bps

  • Property svn:keywords set to Date Rev Author Id
File size: 63.0 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 11212 2010-09-16 02:01:49Z 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 t = 0;
900    int dup = -1;
901    int * tids;
902    tr_benc * val;
903    tr_tracker_info * trackers;
904    tr_bool changed = FALSE;
905    const tr_info * inf = tr_torrentInfo( tor );
906    const char * errmsg = NULL;
907
908    /* make a working copy of the existing announce list */
909    n = inf->trackerCount;
910    tids = tr_new0( int, n );
911    trackers = tr_new0( tr_tracker_info, n );
912    copyTrackers( trackers, inf->trackers, n );
913
914    /* remove the ones specified in the urls list */
915    i = 0;
916    while(( val = tr_bencListChild( ids, i++ )))
917    {
918        int64_t pos;
919        if( tr_bencGetInt( val, &pos ) && pos < n )
920            tids[t++] = pos;
921    }
922
923    /* sort trackerIds and remove from largest to smallest so there is no need to recacluate array indicies */
924    qsort( tids, t, sizeof(int), compareInt );
925    while( t-- )
926    {
927        /* check for duplicates */
928        if( tids[t] == dup )
929            continue;
930        tr_removeElementFromArray( trackers, tids[t], sizeof( tr_tracker_info ), n-- );
931        dup = tids[t];
932        changed = TRUE;
933    }
934
935    if( !changed )
936        errmsg = "invalid argument";
937    else if( !tr_torrentSetAnnounceList( tor, trackers, n ) )
938        errmsg = "error setting announce list";
939
940    freeTrackers( trackers, n );
941    tr_free( tids );
942    return errmsg;
943}
944
945static const char*
946torrentSet( tr_session               * session,
947            tr_benc                  * args_in,
948            tr_benc                  * args_out UNUSED,
949            struct tr_rpc_idle_data  * idle_data UNUSED )
950{
951    const char * errmsg = NULL;
952    int i, torrentCount;
953    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
954
955    assert( idle_data == NULL );
956
957    for( i = 0; i < torrentCount; ++i )
958    {
959        int64_t      tmp;
960        double       d;
961        tr_benc *    files;
962        tr_benc *    trackers;
963        tr_bool      boolVal;
964        tr_torrent * tor = torrents[i];
965
966        if( tr_bencDictFindInt( args_in, "bandwidthPriority", &tmp ) )
967            if( tr_isPriority( tmp ) )
968                tr_torrentSetPriority( tor, tmp );
969        if( !errmsg && tr_bencDictFindList( args_in, "files-unwanted", &files ) )
970            errmsg = setFileDLs( tor, FALSE, files );
971        if( !errmsg && tr_bencDictFindList( args_in, "files-wanted", &files ) )
972            errmsg = setFileDLs( tor, TRUE, files );
973        if( tr_bencDictFindInt( args_in, "peer-limit", &tmp ) )
974            tr_torrentSetPeerLimit( tor, tmp );
975        if( !errmsg &&  tr_bencDictFindList( args_in, "priority-high", &files ) )
976            errmsg = setFilePriorities( tor, TR_PRI_HIGH, files );
977        if( !errmsg && tr_bencDictFindList( args_in, "priority-low", &files ) )
978            errmsg = setFilePriorities( tor, TR_PRI_LOW, files );
979        if( !errmsg && tr_bencDictFindList( args_in, "priority-normal", &files ) )
980            errmsg = setFilePriorities( tor, TR_PRI_NORMAL, files );
981        if( tr_bencDictFindInt( args_in, "downloadLimit", &tmp ) )
982            tr_torrentSetSpeedLimit_KBps( tor, TR_DOWN, tmp );
983        if( tr_bencDictFindBool( args_in, "downloadLimited", &boolVal ) )
984            tr_torrentUseSpeedLimit( tor, TR_DOWN, boolVal );
985        if( tr_bencDictFindBool( args_in, "honorsSessionLimits", &boolVal ) )
986            tr_torrentUseSessionLimits( tor, boolVal );
987        if( tr_bencDictFindInt( args_in, "uploadLimit", &tmp ) )
988            tr_torrentSetSpeedLimit_KBps( tor, TR_UP, tmp );
989        if( tr_bencDictFindBool( args_in, "uploadLimited", &boolVal ) )
990            tr_torrentUseSpeedLimit( tor, TR_UP, boolVal );
991        if( tr_bencDictFindInt( args_in, "seedIdleLimit", &tmp ) )
992            tr_torrentSetIdleLimit( tor, tmp );
993        if( tr_bencDictFindInt( args_in, "seedIdleMode", &tmp ) )
994            tr_torrentSetIdleMode( tor, tmp );
995        if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
996            tr_torrentSetRatioLimit( tor, d );
997        if( tr_bencDictFindInt( args_in, "seedRatioMode", &tmp ) )
998            tr_torrentSetRatioMode( tor, tmp );
999        if( !errmsg && tr_bencDictFindList( args_in, "trackerAdd", &trackers ) )
1000            errmsg = addTrackerUrls( tor, trackers );
1001        if( !errmsg && tr_bencDictFindList( args_in, "trackerRemove", &trackers ) )
1002            errmsg = removeTrackers( tor, trackers );
1003        if( !errmsg && tr_bencDictFindList( args_in, "trackerReplace", &trackers ) )
1004            errmsg = replaceTrackers( tor, trackers );
1005        notify( session, TR_RPC_TORRENT_CHANGED, tor );
1006    }
1007
1008    tr_free( torrents );
1009    return errmsg;
1010}
1011
1012static const char*
1013torrentSetLocation( tr_session               * session,
1014                    tr_benc                  * args_in,
1015                    tr_benc                  * args_out UNUSED,
1016                    struct tr_rpc_idle_data  * idle_data UNUSED )
1017{
1018    const char * errmsg = NULL;
1019    const char * location = NULL;
1020
1021    assert( idle_data == NULL );
1022
1023    if( !tr_bencDictFindStr( args_in, "location", &location ) )
1024    {
1025        errmsg = "no location";
1026    }
1027    else
1028    {
1029        tr_bool move = FALSE;
1030        int i, torrentCount;
1031        tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
1032
1033        tr_bencDictFindBool( args_in, "move", &move );
1034
1035        for( i=0; i<torrentCount; ++i )
1036        {
1037            tr_torrent * tor = torrents[i];
1038            tr_torrentSetLocation( tor, location, move, NULL, NULL );
1039            notify( session, TR_RPC_TORRENT_MOVED, tor );
1040        }
1041
1042        tr_free( torrents );
1043    }
1044
1045    return errmsg;
1046}
1047
1048/***
1049****
1050***/
1051
1052static void
1053portTested( tr_session       * session UNUSED,
1054            long               response_code,
1055            const void       * response,
1056            size_t             response_byte_count,
1057            void             * user_data )
1058{
1059    char result[1024];
1060    struct tr_rpc_idle_data * data = user_data;
1061
1062    if( response_code != 200 )
1063    {
1064        tr_snprintf( result, sizeof( result ), "portTested: http error %ld: %s",
1065                     response_code, tr_webGetResponseStr( response_code ) );
1066    }
1067    else /* success */
1068    {
1069        const tr_bool isOpen = response_byte_count && *(char*)response == '1';
1070        tr_bencDictAddBool( data->args_out, "port-is-open", isOpen );
1071        tr_snprintf( result, sizeof( result ), "success" );
1072    }
1073
1074    tr_idle_function_done( data, result );
1075}
1076
1077static const char*
1078portTest( tr_session               * session,
1079          tr_benc                  * args_in UNUSED,
1080          tr_benc                  * args_out UNUSED,
1081          struct tr_rpc_idle_data  * idle_data )
1082{
1083    const int port = tr_sessionGetPeerPort( session );
1084    char * url = tr_strdup_printf( "http://portcheck.transmissionbt.com/%d", port );
1085    tr_webRun( session, url, NULL, portTested, idle_data );
1086    tr_free( url );
1087    return NULL;
1088}
1089
1090/***
1091****
1092***/
1093
1094static void
1095gotNewBlocklist( tr_session       * session,
1096                 long               response_code,
1097                 const void       * response,
1098                 size_t             response_byte_count,
1099                 void             * user_data )
1100{
1101    char result[1024];
1102    struct tr_rpc_idle_data * data = user_data;
1103
1104    if( response_code != 200 )
1105    {
1106        tr_snprintf( result, sizeof( result ), "gotNewBlocklist: http error %ld: %s",
1107                     response_code, tr_webGetResponseStr( response_code ) );
1108    }
1109    else /* successfully fetched the blocklist... */
1110    {
1111        const char * configDir = tr_sessionGetConfigDir( session );
1112        char * filename = tr_buildPath( configDir, "blocklist.tmp", NULL );
1113        FILE * fp = fopen( filename, "wb+" );
1114
1115        if( fp == NULL )
1116        {
1117            tr_snprintf( result, sizeof( result ),
1118                         _( "Couldn't save file \"%1$s\": %2$s" ),
1119                         filename, tr_strerror( errno ) );
1120        }
1121        else
1122        {
1123            const size_t n = fwrite( response, 1, response_byte_count, fp );
1124            fclose( fp );
1125
1126            if( n != response_byte_count )
1127            {
1128                tr_snprintf( result, sizeof( result ),
1129                             _( "Couldn't save file \"%1$s\": %2$s" ),
1130                             filename, tr_strerror( errno ) );
1131            }
1132            else
1133            {
1134                /* feed it to the session */
1135                const int ruleCount = tr_blocklistSetContent( session, filename );
1136
1137                /* give the client a response */
1138                tr_bencDictAddInt( data->args_out, "blocklist-size", ruleCount );
1139                tr_snprintf( result, sizeof( result ), "success" );
1140            }
1141
1142            /* cleanup */
1143            unlink( filename );
1144        }
1145
1146        /* cleanup */
1147        tr_free( filename );
1148    }
1149
1150    tr_idle_function_done( data, result );
1151}
1152
1153static const char*
1154blocklistUpdate( tr_session               * session,
1155                 tr_benc                  * args_in UNUSED,
1156                 tr_benc                  * args_out UNUSED,
1157                 struct tr_rpc_idle_data  * idle_data )
1158{
1159    const char * url = "http://update.transmissionbt.com/level1";
1160    tr_webRun( session, url, NULL, gotNewBlocklist, idle_data );
1161    return NULL;
1162}
1163
1164/***
1165****
1166***/
1167
1168static void
1169addTorrentImpl( struct tr_rpc_idle_data * data, tr_ctor * ctor )
1170{
1171    int err = 0;
1172    const char * result = NULL;
1173    tr_torrent * tor = tr_torrentNew( ctor, &err );
1174
1175    tr_ctorFree( ctor );
1176
1177    if( tor )
1178    {
1179        tr_benc fields;
1180        tr_bencInitList( &fields, 3 );
1181        tr_bencListAddStr( &fields, "id" );
1182        tr_bencListAddStr( &fields, "name" );
1183        tr_bencListAddStr( &fields, "hashString" );
1184        addInfo( tor, tr_bencDictAdd( data->args_out, "torrent-added" ), &fields );
1185        notify( data->session, TR_RPC_TORRENT_ADDED, tor );
1186        tr_bencFree( &fields );
1187    }
1188    else if( err == TR_PARSE_DUPLICATE )
1189    {
1190        result = "duplicate torrent";
1191    }
1192    else if( err == TR_PARSE_ERR )
1193    {
1194        result = "invalid or corrupt torrent file";
1195    }
1196
1197    tr_idle_function_done( data, result );
1198}
1199
1200
1201struct add_torrent_idle_data
1202{
1203    struct tr_rpc_idle_data * data;
1204    tr_ctor * ctor;
1205};
1206
1207static void
1208gotMetadataFromURL( tr_session       * session UNUSED,
1209                    long               response_code,
1210                    const void       * response,
1211                    size_t             response_byte_count,
1212                    void             * user_data )
1213{
1214    struct add_torrent_idle_data * data = user_data;
1215
1216    dbgmsg( "torrentAdd: HTTP response code was %ld (%s); response length was %zu bytes",
1217            response_code, tr_webGetResponseStr( response_code ), response_byte_count );
1218
1219    if( response_code==200 || response_code==221 ) /* http or ftp success.. */
1220    {
1221        tr_ctorSetMetainfo( data->ctor, response, response_byte_count );
1222        addTorrentImpl( data->data, data->ctor );
1223    }
1224    else
1225    {
1226        char result[1024];
1227        tr_snprintf( result, sizeof( result ), "gotMetadataFromURL: http error %ld: %s",
1228                     response_code, tr_webGetResponseStr( response_code ) );
1229        tr_idle_function_done( data->data, result );
1230    }
1231
1232    tr_free( data );
1233}
1234
1235static tr_bool
1236isCurlURL( const char * filename )
1237{
1238    if( filename == NULL )
1239        return FALSE;
1240
1241    return !strncmp( filename, "ftp://", 6 ) ||
1242           !strncmp( filename, "http://", 7 ) ||
1243           !strncmp( filename, "https://", 8 );
1244}
1245
1246static tr_file_index_t*
1247fileListFromList( tr_benc * list, tr_file_index_t * setmeCount )
1248{
1249    size_t i;
1250    const size_t childCount = tr_bencListSize( list );
1251    tr_file_index_t n = 0;
1252    tr_file_index_t * files = tr_new0( tr_file_index_t, childCount );
1253
1254    for( i=0; i<childCount; ++i ) {
1255        int64_t intVal;
1256        if( tr_bencGetInt( tr_bencListChild( list, i ), &intVal ) )
1257            files[n++] = (tr_file_index_t)intVal;
1258    }
1259
1260    *setmeCount = n;
1261    return files;
1262}
1263
1264static const char*
1265torrentAdd( tr_session               * session,
1266            tr_benc                  * args_in,
1267            tr_benc                  * args_out UNUSED,
1268            struct tr_rpc_idle_data  * idle_data )
1269{
1270    const char * filename = NULL;
1271    const char * metainfo_base64 = NULL;
1272
1273    assert( idle_data != NULL );
1274
1275    tr_bencDictFindStr( args_in, "filename", &filename );
1276    tr_bencDictFindStr( args_in, "metainfo", &metainfo_base64 );
1277    if( !filename && !metainfo_base64 )
1278        return "no filename or metainfo specified";
1279    else
1280    {
1281        int64_t      i;
1282        tr_bool      boolVal;
1283        const char * str;
1284        tr_benc    * l;
1285        tr_ctor    * ctor = tr_ctorNew( session );
1286
1287        /* set the optional arguments */
1288
1289        if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1290            tr_ctorSetDownloadDir( ctor, TR_FORCE, str );
1291
1292        if( tr_bencDictFindBool( args_in, "paused", &boolVal ) )
1293            tr_ctorSetPaused( ctor, TR_FORCE, boolVal );
1294
1295        if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
1296            tr_ctorSetPeerLimit( ctor, TR_FORCE, i );
1297
1298        if( tr_bencDictFindInt( args_in, "bandwidthPriority", &i ) )
1299            tr_ctorSetBandwidthPriority( ctor, i );
1300
1301        if( tr_bencDictFindList( args_in, "files-unwanted", &l ) ) {
1302            tr_file_index_t fileCount;
1303            tr_file_index_t * files = fileListFromList( l, &fileCount );
1304            tr_ctorSetFilesWanted( ctor, files, fileCount, FALSE );
1305            tr_free( files );
1306        }
1307        if( tr_bencDictFindList( args_in, "files-wanted", &l ) ) {
1308            tr_file_index_t fileCount;
1309            tr_file_index_t * files = fileListFromList( l, &fileCount );
1310            tr_ctorSetFilesWanted( ctor, files, fileCount, TRUE );
1311            tr_free( files );
1312        }
1313
1314        if( tr_bencDictFindList( args_in, "priority-low", &l ) ) {
1315            tr_file_index_t fileCount;
1316            tr_file_index_t * files = fileListFromList( l, &fileCount );
1317            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_LOW );
1318            tr_free( files );
1319        }
1320        if( tr_bencDictFindList( args_in, "priority-normal", &l ) ) {
1321            tr_file_index_t fileCount;
1322            tr_file_index_t * files = fileListFromList( l, &fileCount );
1323            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_NORMAL );
1324            tr_free( files );
1325        }
1326        if( tr_bencDictFindList( args_in, "priority-high", &l ) ) {
1327            tr_file_index_t fileCount;
1328            tr_file_index_t * files = fileListFromList( l, &fileCount );
1329            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_HIGH );
1330            tr_free( files );
1331        }
1332
1333        dbgmsg( "torrentAdd: filename is \"%s\"", filename ? filename : "(null)" );
1334
1335        if( isCurlURL( filename ) )
1336        {
1337            struct add_torrent_idle_data * d = tr_new0( struct add_torrent_idle_data, 1 );
1338            d->data = idle_data;
1339            d->ctor = ctor;
1340            tr_webRun( session, filename, NULL, gotMetadataFromURL, d );
1341        }
1342        else
1343        {
1344            char * fname = tr_strstrip( tr_strdup( filename ) );
1345
1346            if( fname == NULL )
1347            {
1348                int len;
1349                char * metainfo = tr_base64_decode( metainfo_base64, -1, &len );
1350                tr_ctorSetMetainfo( ctor, (uint8_t*)metainfo, len );
1351                tr_free( metainfo );
1352            }
1353            else if( !strncmp( fname, "magnet:?", 8 ) )
1354            {
1355                tr_ctorSetMetainfoFromMagnetLink( ctor, fname );
1356            }
1357            else
1358            {
1359                tr_ctorSetMetainfoFromFile( ctor, fname );
1360            }
1361
1362            addTorrentImpl( idle_data, ctor );
1363
1364            tr_free( fname );
1365        }
1366
1367    }
1368
1369    return NULL;
1370}
1371
1372/***
1373****
1374***/
1375
1376static const char*
1377sessionSet( tr_session               * session,
1378            tr_benc                  * args_in,
1379            tr_benc                  * args_out UNUSED,
1380            struct tr_rpc_idle_data  * idle_data UNUSED )
1381{
1382    int64_t      i;
1383    double       d;
1384    tr_bool      boolVal;
1385    const char * str;
1386
1387    assert( idle_data == NULL );
1388
1389    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, &i ) )
1390        tr_sessionSetCacheLimit_MB( session, i );
1391    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_UP_KBps, &i ) )
1392        tr_sessionSetAltSpeed_KBps( session, TR_UP, i );
1393    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, &i ) )
1394        tr_sessionSetAltSpeed_KBps( session, TR_DOWN, i );
1395    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_ENABLED, &boolVal ) )
1396        tr_sessionUseAltSpeed( session, boolVal );
1397    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &i ) )
1398        tr_sessionSetAltSpeedBegin( session, i );
1399    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_END, &i ) )
1400        tr_sessionSetAltSpeedEnd( session, i );
1401    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &i ) )
1402        tr_sessionSetAltSpeedDay( session, i );
1403    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &boolVal ) )
1404        tr_sessionUseAltSpeedTime( session, boolVal );
1405    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_BLOCKLIST_ENABLED, &boolVal ) )
1406        tr_blocklistSetEnabled( session, boolVal );
1407    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1408        tr_sessionSetDownloadDir( session, str );
1409    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_INCOMPLETE_DIR, &str ) )
1410        tr_sessionSetIncompleteDir( session, str );
1411    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, &boolVal ) )
1412        tr_sessionSetIncompleteDirEnabled( session, boolVal );
1413    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &i ) )
1414        tr_sessionSetPeerLimit( session, i );
1415    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i ) )
1416        tr_sessionSetPeerLimitPerTorrent( session, i );
1417    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) )
1418        tr_sessionSetPexEnabled( session, boolVal );
1419    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DHT_ENABLED, &boolVal ) )
1420        tr_sessionSetDHTEnabled( session, boolVal );
1421    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_LPD_ENABLED, &boolVal ) )
1422        tr_sessionSetLPDEnabled( session, boolVal );
1423    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, &boolVal ) )
1424        tr_sessionSetPeerPortRandomOnStart( session, boolVal );
1425    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_PORT, &i ) )
1426        tr_sessionSetPeerPort( session, i );
1427    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) )
1428        tr_sessionSetPortForwardingEnabled( session, boolVal );
1429    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_RENAME_PARTIAL_FILES, &boolVal ) )
1430        tr_sessionSetIncompleteFileNamingEnabled( session, boolVal );
1431    if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
1432        tr_sessionSetRatioLimit( session, d );
1433    if( tr_bencDictFindBool( args_in, "seedRatioLimited", &boolVal ) )
1434        tr_sessionSetRatioLimited( session, boolVal );
1435    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_IDLE_LIMIT, &i ) )
1436        tr_sessionSetIdleLimit( session, i );
1437    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, &boolVal ) )
1438        tr_sessionSetIdleLimited( session, boolVal );
1439    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_START, &boolVal ) )
1440        tr_sessionSetPaused( session, !boolVal );
1441    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, &str ) )
1442        tr_sessionSetTorrentDoneScript( session, str );
1443    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, &boolVal ) )
1444        tr_sessionSetTorrentDoneScriptEnabled( session, boolVal );
1445    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_TRASH_ORIGINAL, &boolVal ) )
1446        tr_sessionSetDeleteSource( session, boolVal );
1447    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_DSPEED_KBps, &i ) )
1448        tr_sessionSetSpeedLimit_KBps( session, TR_DOWN, i );
1449    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DSPEED_ENABLED, &boolVal ) )
1450        tr_sessionLimitSpeed( session, TR_DOWN, boolVal );
1451    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_USPEED_KBps, &i ) )
1452        tr_sessionSetSpeedLimit_KBps( session, TR_UP, i );
1453    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_USPEED_ENABLED, &boolVal ) )
1454        tr_sessionLimitSpeed( session, TR_UP, boolVal );
1455    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_ENCRYPTION, &str ) ) {
1456        if( !strcmp( str, "required" ) )
1457            tr_sessionSetEncryption( session, TR_ENCRYPTION_REQUIRED );
1458        else if( !strcmp( str, "tolerated" ) )
1459            tr_sessionSetEncryption( session, TR_CLEAR_PREFERRED );
1460        else
1461            tr_sessionSetEncryption( session, TR_ENCRYPTION_PREFERRED );
1462    }
1463
1464    notify( session, TR_RPC_SESSION_CHANGED, NULL );
1465
1466    return NULL;
1467}
1468
1469static const char*
1470sessionStats( tr_session               * session,
1471              tr_benc                  * args_in UNUSED,
1472              tr_benc                  * args_out,
1473              struct tr_rpc_idle_data  * idle_data UNUSED )
1474{
1475    int running = 0;
1476    int total = 0;
1477    tr_benc * d;
1478    tr_session_stats currentStats = { 0.0f, 0, 0, 0, 0, 0 };
1479    tr_session_stats cumulativeStats = { 0.0f, 0, 0, 0, 0, 0 };
1480    tr_torrent * tor = NULL;
1481
1482    assert( idle_data == NULL );
1483
1484    while(( tor = tr_torrentNext( session, tor ))) {
1485        ++total;
1486        if( tor->isRunning )
1487            ++running;
1488    }
1489
1490    tr_sessionGetStats( session, &currentStats );
1491    tr_sessionGetCumulativeStats( session, &cumulativeStats );
1492
1493    tr_bencDictAddInt ( args_out, "activeTorrentCount", running );
1494    tr_bencDictAddReal( args_out, "downloadSpeed", tr_sessionGetPieceSpeed_Bps( session, TR_DOWN ) );
1495    tr_bencDictAddInt ( args_out, "pausedTorrentCount", total - running );
1496    tr_bencDictAddInt ( args_out, "torrentCount", total );
1497    tr_bencDictAddReal( args_out, "uploadSpeed", tr_sessionGetPieceSpeed_Bps( session, TR_UP ) );
1498
1499    d = tr_bencDictAddDict( args_out, "cumulative-stats", 5 );
1500    tr_bencDictAddInt( d, "downloadedBytes", cumulativeStats.downloadedBytes );
1501    tr_bencDictAddInt( d, "filesAdded", cumulativeStats.filesAdded );
1502    tr_bencDictAddInt( d, "secondsActive", cumulativeStats.secondsActive );
1503    tr_bencDictAddInt( d, "sessionCount", cumulativeStats.sessionCount );
1504    tr_bencDictAddInt( d, "uploadedBytes", cumulativeStats.uploadedBytes );
1505
1506    d = tr_bencDictAddDict( args_out, "current-stats", 5 );
1507    tr_bencDictAddInt( d, "downloadedBytes", currentStats.downloadedBytes );
1508    tr_bencDictAddInt( d, "filesAdded", currentStats.filesAdded );
1509    tr_bencDictAddInt( d, "secondsActive", currentStats.secondsActive );
1510    tr_bencDictAddInt( d, "sessionCount", currentStats.sessionCount );
1511    tr_bencDictAddInt( d, "uploadedBytes", currentStats.uploadedBytes );
1512
1513    return NULL;
1514}
1515
1516static const char*
1517sessionGet( tr_session               * s,
1518            tr_benc                  * args_in UNUSED,
1519            tr_benc                  * args_out,
1520            struct tr_rpc_idle_data  * idle_data UNUSED )
1521{
1522    const char * str;
1523    tr_benc *    d = args_out;
1524
1525    assert( idle_data == NULL );
1526    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP_KBps, tr_sessionGetAltSpeed_KBps(s,TR_UP) );
1527    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, tr_sessionGetAltSpeed_KBps(s,TR_DOWN) );
1528    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED, tr_sessionUsesAltSpeed(s) );
1529    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, tr_sessionGetAltSpeedBegin(s) );
1530    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,tr_sessionGetAltSpeedEnd(s) );
1531    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,tr_sessionGetAltSpeedDay(s) );
1532    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, tr_sessionUsesAltSpeedTime(s) );
1533    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, tr_blocklistIsEnabled( s ) );
1534    tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, tr_sessionGetCacheLimit_MB( s ) );
1535    tr_bencDictAddInt ( d, "blocklist-size", tr_blocklistGetRuleCount( s ) );
1536    tr_bencDictAddStr ( d, "config-dir", tr_sessionGetConfigDir( s ) );
1537    tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR, tr_sessionGetDownloadDir( s ) );
1538    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, tr_sessionGetPeerLimit( s ) );
1539    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT, tr_sessionGetPeerLimitPerTorrent( s ) );
1540    tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR, tr_sessionGetIncompleteDir( s ) );
1541    tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, tr_sessionIsIncompleteDirEnabled( s ) );
1542    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED, tr_sessionIsPexEnabled( s ) );
1543    tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED, tr_sessionIsDHTEnabled( s ) );
1544    tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED, tr_sessionIsLPDEnabled( s ) );
1545    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT, tr_sessionGetPeerPort( s ) );
1546    tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, tr_sessionGetPeerPortRandomOnStart( s ) );
1547    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) );
1548    tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES, tr_sessionIsIncompleteFileNamingEnabled( s ) );
1549    tr_bencDictAddInt ( d, "rpc-version", RPC_VERSION );
1550    tr_bencDictAddInt ( d, "rpc-version-minimum", RPC_VERSION_MIN );
1551    tr_bencDictAddReal( d, "seedRatioLimit", tr_sessionGetRatioLimit( s ) );
1552    tr_bencDictAddBool( d, "seedRatioLimited", tr_sessionIsRatioLimited( s ) );
1553    tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT, tr_sessionGetIdleLimit( s ) );
1554    tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, tr_sessionIsIdleLimited( s ) );
1555    tr_bencDictAddBool( d, TR_PREFS_KEY_START, !tr_sessionGetPaused( s ) );
1556    tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL, tr_sessionGetDeleteSource( s ) );
1557    tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_UP ) );
1558    tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_UP ) );
1559    tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_DOWN ) );
1560    tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_DOWN ) );
1561    tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, tr_sessionGetTorrentDoneScript( s ) );
1562    tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, tr_sessionIsTorrentDoneScriptEnabled( s ) );
1563    tr_bencDictAddStr ( d, "version", LONG_VERSION_STRING );
1564    switch( tr_sessionGetEncryption( s ) ) {
1565        case TR_CLEAR_PREFERRED: str = "tolerated"; break;
1566        case TR_ENCRYPTION_REQUIRED: str = "required"; break;
1567        default: str = "preferred"; break;
1568    }
1569    tr_bencDictAddStr( d, TR_PREFS_KEY_ENCRYPTION, str );
1570
1571    return NULL;
1572}
1573
1574/***
1575****
1576***/
1577
1578typedef const char* ( *handler )( tr_session*, tr_benc*, tr_benc*, struct tr_rpc_idle_data * );
1579
1580static struct method
1581{
1582    const char *  name;
1583    tr_bool       immediate;
1584    handler       func;
1585}
1586methods[] =
1587{
1588    { "port-test",             FALSE, portTest            },
1589    { "blocklist-update",      FALSE, blocklistUpdate     },
1590    { "session-get",           TRUE,  sessionGet          },
1591    { "session-set",           TRUE,  sessionSet          },
1592    { "session-stats",         TRUE,  sessionStats        },
1593    { "torrent-add",           FALSE, torrentAdd          },
1594    { "torrent-get",           TRUE,  torrentGet          },
1595    { "torrent-remove",        TRUE,  torrentRemove       },
1596    { "torrent-set",           TRUE,  torrentSet          },
1597    { "torrent-set-location",  TRUE,  torrentSetLocation  },
1598    { "torrent-start",         TRUE,  torrentStart        },
1599    { "torrent-stop",          TRUE,  torrentStop         },
1600    { "torrent-verify",        TRUE,  torrentVerify       },
1601    { "torrent-reannounce",    TRUE,  torrentReannounce   }
1602};
1603
1604static void
1605noop_response_callback( tr_session * session UNUSED,
1606                        const char * response UNUSED,
1607                        size_t       response_len UNUSED,
1608                        void       * user_data UNUSED )
1609{
1610}
1611
1612static void
1613request_exec( tr_session             * session,
1614              tr_benc                * request,
1615              tr_rpc_response_func     callback,
1616              void                   * callback_user_data )
1617{
1618    int i;
1619    const char * str;
1620    tr_benc * args_in = tr_bencDictFind( request, "arguments" );
1621    const char * result = NULL;
1622
1623    if( callback == NULL )
1624        callback = noop_response_callback;
1625
1626    /* parse the request */
1627    if( !tr_bencDictFindStr( request, "method", &str ) )
1628        result = "no method name";
1629    else {
1630        const int n = TR_N_ELEMENTS( methods );
1631        for( i = 0; i < n; ++i )
1632            if( !strcmp( str, methods[i].name ) )
1633                break;
1634        if( i ==n )
1635            result = "method name not recognized";
1636    }
1637
1638    /* if we couldn't figure out which method to use, return an error */
1639    if( result != NULL )
1640    {
1641        int64_t tag;
1642        tr_benc response;
1643        struct evbuffer * buf = evbuffer_new( );
1644
1645        tr_bencInitDict( &response, 3 );
1646        tr_bencDictAddDict( &response, "arguments", 0 );
1647        tr_bencDictAddStr( &response, "result", result );
1648        if( tr_bencDictFindInt( request, "tag", &tag ) )
1649            tr_bencDictAddInt( &response, "tag", tag );
1650        tr_bencToBuf( &response, TR_FMT_JSON_LEAN, buf );
1651        (*callback)( session, (const char*)EVBUFFER_DATA(buf),
1652                     EVBUFFER_LENGTH( buf ), callback_user_data );
1653
1654        evbuffer_free( buf );
1655        tr_bencFree( &response );
1656    }
1657    else if( methods[i].immediate )
1658    {
1659        int64_t tag;
1660        tr_benc response;
1661        tr_benc * args_out;
1662        struct evbuffer * buf = evbuffer_new( );
1663
1664        tr_bencInitDict( &response, 3 );
1665        args_out = tr_bencDictAddDict( &response, "arguments", 0 );
1666        result = (*methods[i].func)( session, args_in, args_out, NULL );
1667        if( result == NULL )
1668            result = "success";
1669        tr_bencDictAddStr( &response, "result", result );
1670        if( tr_bencDictFindInt( request, "tag", &tag ) )
1671            tr_bencDictAddInt( &response, "tag", tag );
1672        tr_bencToBuf( &response, TR_FMT_JSON_LEAN, buf );
1673        (*callback)( session, (const char*)EVBUFFER_DATA(buf),
1674                     EVBUFFER_LENGTH(buf), callback_user_data );
1675
1676        evbuffer_free( buf );
1677        tr_bencFree( &response );
1678    }
1679    else
1680    {
1681        int64_t tag;
1682        struct tr_rpc_idle_data * data = tr_new0( struct tr_rpc_idle_data, 1 );
1683        data->session = session;
1684        data->response = tr_new0( tr_benc, 1 );
1685        tr_bencInitDict( data->response, 3 );
1686        if( tr_bencDictFindInt( request, "tag", &tag ) )
1687            tr_bencDictAddInt( data->response, "tag", tag );
1688        data->args_out = tr_bencDictAddDict( data->response, "arguments", 0 );
1689        data->callback = callback;
1690        data->callback_user_data = callback_user_data;
1691        (*methods[i].func)( session, args_in, data->args_out, data );
1692    }
1693}
1694
1695void
1696tr_rpc_request_exec_json( tr_session            * session,
1697                          const void            * request_json,
1698                          int                     request_len,
1699                          tr_rpc_response_func    callback,
1700                          void                  * callback_user_data )
1701{
1702    tr_benc top;
1703    int have_content;
1704
1705    if( request_len < 0 )
1706        request_len = strlen( request_json );
1707
1708    have_content = !tr_jsonParse( "rpc", request_json, request_len, &top, NULL );
1709    request_exec( session, have_content ? &top : NULL, callback, callback_user_data );
1710
1711    if( have_content )
1712        tr_bencFree( &top );
1713}
1714
1715/**
1716 * Munge the URI into a usable form.
1717 *
1718 * We have very loose typing on this to make the URIs as simple as possible:
1719 * - anything not a 'tag' or 'method' is automatically in 'arguments'
1720 * - values that are all-digits are numbers
1721 * - values that are all-digits or commas are number lists
1722 * - all other values are strings
1723 */
1724void
1725tr_rpc_parse_list_str( tr_benc     * setme,
1726                       const char  * str,
1727                       int           len )
1728
1729{
1730    int valueCount;
1731    int * values = tr_parseNumberRange( str, len, &valueCount );
1732
1733    if( valueCount == 0 )
1734        tr_bencInitStr( setme, str, len );
1735    else if( valueCount == 1 )
1736        tr_bencInitInt( setme, values[0] );
1737    else {
1738        int i;
1739        tr_bencInitList( setme, valueCount );
1740        for( i=0; i<valueCount; ++i )
1741            tr_bencListAddInt( setme, values[i] );
1742    }
1743
1744    tr_free( values );
1745}
1746
1747void
1748tr_rpc_request_exec_uri( tr_session           * session,
1749                         const void           * request_uri,
1750                         int                    request_len,
1751                         tr_rpc_response_func   callback,
1752                         void                 * callback_user_data )
1753{
1754    tr_benc      top, * args;
1755    char *       request = tr_strndup( request_uri, request_len );
1756    const char * pch;
1757
1758    tr_bencInitDict( &top, 3 );
1759    args = tr_bencDictAddDict( &top, "arguments", 0 );
1760
1761    pch = strchr( request, '?' );
1762    if( !pch ) pch = request;
1763    while( pch )
1764    {
1765        const char * delim = strchr( pch, '=' );
1766        const char * next = strchr( pch, '&' );
1767        if( delim )
1768        {
1769            char *    key = tr_strndup( pch, delim - pch );
1770            int       isArg = strcmp( key, "method" ) && strcmp( key, "tag" );
1771            tr_benc * parent = isArg ? args : &top;
1772            tr_rpc_parse_list_str( tr_bencDictAdd( parent, key ),
1773                                  delim + 1,
1774                                  next ? (size_t)(
1775                                       next -
1776                                      ( delim + 1 ) ) : strlen( delim + 1 ) );
1777            tr_free( key );
1778        }
1779        pch = next ? next + 1 : NULL;
1780    }
1781
1782    request_exec( session, &top, callback, callback_user_data );
1783
1784    /* cleanup */
1785    tr_bencFree( &top );
1786    tr_free( request );
1787}
Note: See TracBrowser for help on using the repository browser.