source: trunk/libtransmission/rpcimpl.c @ 10931

Last change on this file since 10931 was 10931, checked in by charles, 12 years ago

(trunk) #3045 "make libtransmission's API byte-oriented instead of KiB-oriented." -- implemented. This is a largish commit and will break the mac build for a little while.

  • Property svn:keywords set to Date Rev Author Id
File size: 63.1 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 10931 2010-07-03 00:25:22Z charles $
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     9
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, 25 );
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_bencDictAddInt ( d, "scrapeState", s->scrapeState );
413        tr_bencDictAddInt ( d, "seederCount", s->seederCount );
414        tr_bencDictAddInt ( d, "tier", s->tier );
415    }
416}
417
418static void
419addPeers( const tr_torrent * tor,
420          tr_benc *          list )
421{
422    int            i;
423    int            peerCount;
424    tr_peer_stat * peers = tr_torrentPeers( tor, &peerCount );
425
426    tr_bencInitList( list, peerCount );
427
428    for( i = 0; i < peerCount; ++i )
429    {
430        tr_benc *            d = tr_bencListAddDict( list, 14 );
431        const tr_peer_stat * peer = peers + i;
432        tr_bencDictAddStr ( d, "address", peer->addr );
433        tr_bencDictAddStr ( d, "clientName", peer->client );
434        tr_bencDictAddBool( d, "clientIsChoked", peer->clientIsChoked );
435        tr_bencDictAddBool( d, "clientIsInterested", peer->clientIsInterested );
436        tr_bencDictAddStr ( d, "flagStr", peer->flagStr );
437        tr_bencDictAddBool( d, "isDownloadingFrom", peer->isDownloadingFrom );
438        tr_bencDictAddBool( d, "isEncrypted", peer->isEncrypted );
439        tr_bencDictAddBool( d, "isIncoming", peer->isIncoming );
440        tr_bencDictAddBool( d, "isUploadingTo", peer->isUploadingTo );
441        tr_bencDictAddBool( d, "peerIsChoked", peer->peerIsChoked );
442        tr_bencDictAddBool( d, "peerIsInterested", peer->peerIsInterested );
443        tr_bencDictAddInt ( d, "port", peer->port );
444        tr_bencDictAddReal( d, "progress", peer->progress );
445        tr_bencDictAddInt ( d, "rateToClient", peer->rateToClient_Bps );
446        tr_bencDictAddInt ( d, "rateToPeer", peer->rateToPeer_Bps );
447    }
448
449    tr_torrentPeersFree( peers, peerCount );
450}
451
452/* faster-than-strcmp() optimization.  this is kind of clumsy,
453   but addField() was in the profiler's top 10 list, and this
454   makes it 4x faster... */
455#define tr_streq(a,alen,b) ((alen+1==sizeof(b)) && !memcmp(a,b,alen))
456
457static void
458addField( const tr_torrent * tor, tr_benc * d, const char * key )
459{
460    const tr_info * inf = tr_torrentInfo( tor );
461    const tr_stat * st = tr_torrentStat( (tr_torrent*)tor );
462    const size_t keylen = strlen( key );
463
464    if( tr_streq( key, keylen, "activityDate" ) )
465        tr_bencDictAddInt( d, key, st->activityDate );
466    else if( tr_streq( key, keylen, "addedDate" ) )
467        tr_bencDictAddInt( d, key, st->addedDate );
468    else if( tr_streq( key, keylen, "bandwidthPriority" ) )
469        tr_bencDictAddInt( d, key, tr_torrentGetPriority( tor ) );
470    else if( tr_streq( key, keylen, "comment" ) )
471        tr_bencDictAddStr( d, key, inf->comment ? inf->comment : "" );
472    else if( tr_streq( key, keylen, "corruptEver" ) )
473        tr_bencDictAddInt( d, key, st->corruptEver );
474    else if( tr_streq( key, keylen, "creator" ) )
475        tr_bencDictAddStr( d, key, inf->creator ? inf->creator : "" );
476    else if( tr_streq( key, keylen, "dateCreated" ) )
477        tr_bencDictAddInt( d, key, inf->dateCreated );
478    else if( tr_streq( key, keylen, "desiredAvailable" ) )
479        tr_bencDictAddInt( d, key, st->desiredAvailable );
480    else if( tr_streq( key, keylen, "doneDate" ) )
481        tr_bencDictAddInt( d, key, st->doneDate );
482    else if( tr_streq( key, keylen, "downloadDir" ) )
483        tr_bencDictAddStr( d, key, tr_torrentGetDownloadDir( tor ) );
484    else if( tr_streq( key, keylen, "downloadedEver" ) )
485        tr_bencDictAddInt( d, key, st->downloadedEver );
486    else if( tr_streq( key, keylen, "downloadLimit" ) )
487        tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit_Bps( tor, TR_DOWN ) );
488    else if( tr_streq( key, keylen, "downloadLimited" ) )
489        tr_bencDictAddBool( d, key, tr_torrentUsesSpeedLimit( tor, TR_DOWN ) );
490    else if( tr_streq( key, keylen, "error" ) )
491        tr_bencDictAddInt( d, key, st->error );
492    else if( tr_streq( key, keylen, "errorString" ) )
493        tr_bencDictAddStr( d, key, st->errorString );
494    else if( tr_streq( key, keylen, "eta" ) )
495        tr_bencDictAddInt( d, key, st->eta );
496    else if( tr_streq( key, keylen, "files" ) )
497        addFiles( tor, tr_bencDictAddList( d, key, inf->fileCount ) );
498    else if( tr_streq( key, keylen, "fileStats" ) )
499        addFileStats( tor, tr_bencDictAddList( d, key, inf->fileCount ) );
500    else if( tr_streq( key, keylen, "hashString" ) )
501        tr_bencDictAddStr( d, key, tor->info.hashString );
502    else if( tr_streq( key, keylen, "haveUnchecked" ) )
503        tr_bencDictAddInt( d, key, st->haveUnchecked );
504    else if( tr_streq( key, keylen, "haveValid" ) )
505        tr_bencDictAddInt( d, key, st->haveValid );
506    else if( tr_streq( key, keylen, "honorsSessionLimits" ) )
507        tr_bencDictAddBool( d, key, tr_torrentUsesSessionLimits( tor ) );
508    else if( tr_streq( key, keylen, "id" ) )
509        tr_bencDictAddInt( d, key, st->id );
510    else if( tr_streq( key, keylen, "isFinished" ) )
511        tr_bencDictAddBool( d, key, st->finished );
512    else if( tr_streq( key, keylen, "isPrivate" ) )
513        tr_bencDictAddBool( d, key, tr_torrentIsPrivate( tor ) );
514    else if( tr_streq( key, keylen, "leftUntilDone" ) )
515        tr_bencDictAddInt( d, key, st->leftUntilDone );
516    else if( tr_streq( key, keylen, "manualAnnounceTime" ) )
517        tr_bencDictAddInt( d, key, st->manualAnnounceTime );
518    else if( tr_streq( key, keylen, "maxConnectedPeers" ) )
519        tr_bencDictAddInt( d, key,  tr_torrentGetPeerLimit( tor ) );
520    else if( tr_streq( key, keylen, "magnetLink" ) ) {
521        char * str = tr_torrentGetMagnetLink( tor );
522        tr_bencDictAddStr( d, key, str );
523        tr_free( str );
524    }
525    else if( tr_streq( key, keylen, "metadataPercentComplete" ) )
526        tr_bencDictAddReal( d, key, st->metadataPercentComplete );
527    else if( tr_streq( key, keylen, "name" ) )
528        tr_bencDictAddStr( d, key, inf->name );
529    else if( tr_streq( key, keylen, "percentDone" ) )
530        tr_bencDictAddReal( d, key, st->percentDone );
531    else if( tr_streq( key, keylen, "peer-limit" ) )
532        tr_bencDictAddInt( d, key, tr_torrentGetPeerLimit( tor ) );
533    else if( tr_streq( key, keylen, "peers" ) )
534        addPeers( tor, tr_bencDictAdd( d, key ) );
535    else if( tr_streq( key, keylen, "peersConnected" ) )
536        tr_bencDictAddInt( d, key, st->peersConnected );
537    else if( tr_streq( key, keylen, "peersFrom" ) )
538    {
539        tr_benc *   tmp = tr_bencDictAddDict( d, key, 6 );
540        const int * f = st->peersFrom;
541        tr_bencDictAddInt( tmp, "fromCache",    f[TR_PEER_FROM_RESUME] );
542        tr_bencDictAddInt( tmp, "fromDht",      f[TR_PEER_FROM_DHT] );
543        tr_bencDictAddInt( tmp, "fromIncoming", f[TR_PEER_FROM_INCOMING] );
544        tr_bencDictAddInt( tmp, "fromLtep",     f[TR_PEER_FROM_LTEP] );
545        tr_bencDictAddInt( tmp, "fromPex",      f[TR_PEER_FROM_PEX] );
546        tr_bencDictAddInt( tmp, "fromTracker",  f[TR_PEER_FROM_TRACKER] );
547    }
548    else if( tr_streq( key, keylen, "peersGettingFromUs" ) )
549        tr_bencDictAddInt( d, key, st->peersGettingFromUs );
550    else if( tr_streq( key, keylen, "peersKnown" ) )
551        tr_bencDictAddInt( d, key, st->peersKnown );
552    else if( tr_streq( key, keylen, "peersSendingToUs" ) )
553        tr_bencDictAddInt( d, key, st->peersSendingToUs );
554    else if( tr_streq( key, keylen, "pieces" ) ) {
555        const tr_bitfield * pieces = tr_cpPieceBitfield( &tor->completion );
556        char * str = tr_base64_encode( pieces->bits, pieces->byteCount, NULL );
557        tr_bencDictAddStr( d, key, str!=NULL ? str : "" );
558        tr_free( str );
559    }
560    else if( tr_streq( key, keylen, "pieceCount" ) )
561        tr_bencDictAddInt( d, key, inf->pieceCount );
562    else if( tr_streq( key, keylen, "pieceSize" ) )
563        tr_bencDictAddInt( d, key, inf->pieceSize );
564    else if( tr_streq( key, keylen, "priorities" ) )
565    {
566        tr_file_index_t i;
567        tr_benc *       p = tr_bencDictAddList( d, key, inf->fileCount );
568        for( i = 0; i < inf->fileCount; ++i )
569            tr_bencListAddInt( p, inf->files[i].priority );
570    }
571    else if( tr_streq( key, keylen, "rateDownload" ) )
572        tr_bencDictAddInt( d, key, st->pieceDownloadSpeed_Bps );
573    else if( tr_streq( key, keylen, "rateUpload" ) )
574        tr_bencDictAddInt( d, key, st->pieceUploadSpeed_Bps );
575    else if( tr_streq( key, keylen, "recheckProgress" ) )
576        tr_bencDictAddReal( d, key, st->recheckProgress );
577    else if( tr_streq( key, keylen, "seedRatioLimit" ) )
578        tr_bencDictAddReal( d, key, tr_torrentGetRatioLimit( tor ) );
579    else if( tr_streq( key, keylen, "seedRatioMode" ) )
580        tr_bencDictAddInt( d, key, tr_torrentGetRatioMode( tor ) );
581    else if( tr_streq( key, keylen, "sizeWhenDone" ) )
582        tr_bencDictAddInt( d, key, st->sizeWhenDone );
583    else if( tr_streq( key, keylen, "startDate" ) )
584        tr_bencDictAddInt( d, key, st->startDate );
585    else if( tr_streq( key, keylen, "status" ) )
586        tr_bencDictAddInt( d, key, st->activity );
587    else if( tr_streq( key, keylen, "trackers" ) )
588        addTrackers( inf, tr_bencDictAddList( d, key, inf->trackerCount ) );
589    else if( tr_streq( key, keylen, "trackerStats" ) ) {
590        int n;
591        tr_tracker_stat * s = tr_torrentTrackers( tor, &n );
592        addTrackerStats( s, n, tr_bencDictAddList( d, key, n ) );
593        tr_torrentTrackersFree( s, n );
594    }
595    else if( tr_streq( key, keylen, "torrentFile" ) )
596        tr_bencDictAddStr( d, key, inf->torrent );
597    else if( tr_streq( key, keylen, "totalSize" ) )
598        tr_bencDictAddInt( d, key, inf->totalSize );
599    else if( tr_streq( key, keylen, "uploadedEver" ) )
600        tr_bencDictAddInt( d, key, st->uploadedEver );
601    else if( tr_streq( key, keylen, "uploadLimit" ) )
602        tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit_Bps( tor, TR_UP ) );
603    else if( tr_streq( key, keylen, "uploadLimited" ) )
604        tr_bencDictAddBool( d, key, tr_torrentUsesSpeedLimit( tor, TR_UP ) );
605    else if( tr_streq( key, keylen, "uploadRatio" ) )
606        tr_bencDictAddReal( d, key, st->ratio );
607    else if( tr_streq( key, keylen, "wanted" ) )
608    {
609        tr_file_index_t i;
610        tr_benc *       w = tr_bencDictAddList( d, key, inf->fileCount );
611        for( i = 0; i < inf->fileCount; ++i )
612            tr_bencListAddInt( w, inf->files[i].dnd ? 0 : 1 );
613    }
614    else if( tr_streq( key, keylen, "webseeds" ) )
615        addWebseeds( inf, tr_bencDictAddList( d, key, inf->webseedCount ) );
616    else if( tr_streq( key, keylen, "webseedsSendingToUs" ) )
617        tr_bencDictAddInt( d, key, st->webseedsSendingToUs );
618}
619
620static void
621addInfo( const tr_torrent * tor,
622         tr_benc *          d,
623         tr_benc *          fields )
624{
625    int          i;
626    const int    n = tr_bencListSize( fields );
627    const char * str;
628
629    tr_bencInitDict( d, n );
630
631    for( i = 0; i < n; ++i )
632        if( tr_bencGetStr( tr_bencListChild( fields, i ), &str ) )
633            addField( tor, d, str );
634}
635
636static const char*
637torrentGet( tr_session               * session,
638            tr_benc                  * args_in,
639            tr_benc                  * args_out,
640            struct tr_rpc_idle_data  * idle_data UNUSED )
641{
642    int           i, torrentCount;
643    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
644    tr_benc *     list = tr_bencDictAddList( args_out, "torrents", torrentCount );
645    tr_benc *     fields;
646    const char *  msg = NULL;
647    const char *  strVal;
648
649    assert( idle_data == NULL );
650
651    if( tr_bencDictFindStr( args_in, "ids", &strVal ) && !strcmp( strVal, "recently-active" ) ) {
652        int n = 0;
653        tr_benc * d;
654        const time_t now = tr_time( );
655        const int interval = RECENTLY_ACTIVE_SECONDS;
656        tr_benc * removed_out = tr_bencDictAddList( args_out, "removed", 0 );
657        while(( d = tr_bencListChild( &session->removedTorrents, n++ ))) {
658            int64_t intVal;
659            if( tr_bencDictFindInt( d, "date", &intVal ) && ( intVal >= now - interval ) ) {
660                tr_bencDictFindInt( d, "id", &intVal );
661                tr_bencListAddInt( removed_out, intVal );
662            }
663        }
664    }
665
666    if( !tr_bencDictFindList( args_in, "fields", &fields ) )
667        msg = "no fields specified";
668    else for( i = 0; i < torrentCount; ++i )
669        addInfo( torrents[i], tr_bencListAdd( list ), fields );
670
671    tr_free( torrents );
672    return msg;
673}
674
675/***
676****
677***/
678
679static const char*
680setFilePriorities( tr_torrent * tor,
681                   int          priority,
682                   tr_benc *    list )
683{
684    int i;
685    int64_t tmp;
686    int fileCount = 0;
687    const int n = tr_bencListSize( list );
688    const char * errmsg = NULL;
689    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
690
691    if( n )
692    {
693        for( i = 0; i < n; ++i ) {
694            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) {
695                if( 0 <= tmp && tmp < tor->info.fileCount ) {
696                    files[fileCount++] = tmp;
697                } else {
698                    errmsg = "file index out of range";
699                }
700            }
701        }
702    }
703    else /* if empty set, apply to all */
704    {
705        tr_file_index_t t;
706        for( t = 0; t < tor->info.fileCount; ++t )
707            files[fileCount++] = t;
708    }
709
710    if( fileCount )
711        tr_torrentSetFilePriorities( tor, files, fileCount, priority );
712
713    tr_free( files );
714    return errmsg;
715}
716
717static const char*
718setFileDLs( tr_torrent * tor,
719            int          do_download,
720            tr_benc *    list )
721{
722    int i;
723    int64_t tmp;
724    int fileCount = 0;
725    const int n = tr_bencListSize( list );
726    const char * errmsg = NULL;
727    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
728
729    if( n ) /* if argument list, process them */
730    {
731        for( i = 0; i < n; ++i ) {
732            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) {
733                if( 0 <= tmp && tmp < tor->info.fileCount ) {
734                    files[fileCount++] = tmp;
735                } else {
736                    errmsg = "file index out of range";
737                }
738            }
739        }
740    }
741    else /* if empty set, apply to all */
742    {
743        tr_file_index_t t;
744        for( t = 0; t < tor->info.fileCount; ++t )
745            files[fileCount++] = t;
746    }
747
748    if( fileCount )
749        tr_torrentSetFileDLs( tor, files, fileCount, do_download );
750
751    tr_free( files );
752    return errmsg;
753}
754
755static tr_bool
756findTrackerById( const tr_info * inf,
757                 uint32_t id,
758                 int * index )
759{
760    int i;
761    tr_bool found = FALSE;
762
763    for( i = 0; i < inf->trackerCount; ++i )
764    {
765        const tr_tracker_info * t = &inf->trackers[i];
766        if( t->id == id )
767        {
768            if( index ) *index = i;
769            found = TRUE;
770            break;
771        }
772    }
773
774    return found;
775}
776
777static tr_bool
778findTrackerByURL( const tr_info * inf,
779                  const char * url,
780                  int * index )
781{
782    int i;
783    tr_bool found = FALSE;
784
785    for( i = 0; i < inf->trackerCount; ++i )
786    {
787        const tr_tracker_info * t = &inf->trackers[i];
788        if( !strcmp( t->announce, url ) )
789        {
790            if( index ) *index = i;
791            found = TRUE;
792            break;
793        }
794    }
795
796    return found;
797}
798
799static const char*
800addTracker( tr_torrent * tor,
801            tr_benc    * tracker )
802{
803    int i;
804    int64_t tmp;
805    tr_bool duplicate = FALSE;
806    const char * errmsg = NULL;
807    const char * announce;
808    const tr_info * inf = tr_torrentInfo( tor );
809
810    if( !tr_bencDictFindStr( tracker, "announce", &announce ) )
811        return "no announce url supplied";
812
813    duplicate = findTrackerByURL( inf, announce, NULL );
814
815    if( !duplicate )
816    {
817        int tier, trackerCount;
818        tr_tracker_info * trackers = tr_new0( tr_tracker_info, inf->trackerCount + 1 );
819
820        if( tr_bencDictFindInt( tracker, "tier", &tmp ) )
821            tier = (int)tmp;
822        else
823            tier = -1;
824
825        for( i = 0; i < inf->trackerCount; ++i )
826        {
827            const tr_tracker_info * t = &inf->trackers[i];
828            trackers[i].tier = t->tier;
829            trackers[i].announce = tr_strdup( t->announce );
830        }
831        trackers[i].tier = tier < 0 ? trackers[i-1].tier + 1 : tier;
832        trackers[i].announce = tr_strdup( announce );
833        trackerCount = inf->trackerCount + 1;
834
835        if( !tr_torrentSetAnnounceList( tor, trackers, trackerCount ) )
836            errmsg = "tracker URL was invalid";
837
838        for( i = 0; i < trackerCount; ++i )
839            tr_free( trackers[i].announce );
840        tr_free( trackers );
841    }
842    else
843        errmsg = "tracker already exists";
844
845    return errmsg;
846}
847
848static const char*
849editTracker( tr_torrent * tor,
850             tr_benc    * tracker )
851{
852    int trackerIndex;
853    int64_t tmp;
854    tr_bool found = FALSE;
855    const char * errmsg = NULL;
856    const char * announce;
857    const tr_info * inf = tr_torrentInfo( tor );
858
859    if( tr_bencDictFindInt( tracker, "id", &tmp ) )
860        found = findTrackerById( inf, (uint32_t)tmp, &trackerIndex );
861    else if( tr_bencDictFindStr( tracker, "announce", &announce ) )
862        found = findTrackerByURL( inf, announce, &trackerIndex );
863    else
864        errmsg = "no tracker supplied";
865
866    if( found )
867    {
868        int tier;
869        const char * new;
870        tr_bool rename = FALSE;
871        tr_bool move = FALSE;
872
873        if( tr_bencDictFindStr( tracker, "announce-new", &new ) )
874        {
875            rename = !findTrackerByURL( inf, new, NULL );
876            if( !rename )
877                errmsg = "tracker already exists";
878        }
879        if( tr_bencDictFindInt( tracker, "tier", &tmp ) )
880        {
881            tier = (int)tmp;
882            move = TRUE;
883        }
884
885        if( ( rename || move ) && !errmsg )
886        {
887            int i, trackerCount;
888            tr_tracker_info * trackers = tr_new0( tr_tracker_info, inf->trackerCount );
889
890            for( i = 0; i < inf->trackerCount; ++i )
891            {
892                const tr_tracker_info * t = &inf->trackers[i];
893                if( i != trackerIndex )
894                {
895                    trackers[i].tier = t->tier;
896                    trackers[i].announce = tr_strdup( t->announce );
897                }
898                else
899                {
900                    trackers[i].tier = move ? tier : t->tier;
901                    trackers[i].announce = tr_strdup( rename ? new : t->announce );
902                }
903            }
904            trackerCount = i;
905
906            if( !tr_torrentSetAnnounceList( tor, trackers, trackerCount ) )
907                errmsg = "error setting announce list";
908
909            for( i = 0; i < trackerCount; ++i )
910                tr_free( trackers[i].announce );
911            tr_free( trackers );
912        }
913        else if( !errmsg )
914            errmsg = "no operation supplied";
915    }
916    else
917        errmsg = "tracker doesn't exists";
918
919    return errmsg;
920}
921
922static const char*
923removeTracker( tr_torrent * tor,
924               tr_benc * tracker  )
925{
926    int trackerIndex;
927    int64_t tmp;
928    tr_bool found = FALSE;
929    const char * errmsg = NULL;
930    const char * announce;
931    const tr_info * inf = tr_torrentInfo( tor );
932
933    if( tr_bencDictFindInt( tracker, "id", &tmp ) )
934        found = findTrackerById( inf, (uint32_t)tmp, &trackerIndex );
935    else if( tr_bencDictFindStr( tracker, "announce", &announce ) )
936        found = findTrackerByURL( inf, announce, &trackerIndex );
937    else
938        errmsg = "no tracker supplied";
939
940    if( found )
941    {
942        int i, j, trackerCount;
943        tr_tracker_info * trackers = tr_new0( tr_tracker_info, inf->trackerCount - 1 );
944
945        for( i = 0, j = 0; i < inf->trackerCount; ++i )
946        {
947            if( i != trackerIndex )
948            {
949                const tr_tracker_info * t = &inf->trackers[i];
950                trackers[j].tier = t->tier;
951                trackers[j].announce = tr_strdup( t->announce );
952                ++j;
953            }
954        }
955        trackerCount = j;
956
957        if( !tr_torrentSetAnnounceList( tor, trackers, trackerCount ) )
958            errmsg = "error setting announce list";
959
960        for( i = 0; i < trackerCount; ++i )
961            tr_free( trackers[i].announce );
962        tr_free( trackers );
963    }
964    else
965        errmsg = "tracker doesn't exists";
966
967    return errmsg;
968}
969
970static const char*
971torrentSet( tr_session               * session,
972            tr_benc                  * args_in,
973            tr_benc                  * args_out UNUSED,
974            struct tr_rpc_idle_data  * idle_data UNUSED )
975{
976    const char * errmsg = NULL;
977    int i, torrentCount;
978    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
979
980    assert( idle_data == NULL );
981
982    for( i = 0; i < torrentCount; ++i )
983    {
984        int64_t      tmp;
985        double       d;
986        tr_benc *    files;
987        tr_benc *    tracker;
988        tr_bool      boolVal;
989        tr_torrent * tor = torrents[i];
990
991        if( tr_bencDictFindInt( args_in, "bandwidthPriority", &tmp ) )
992            if( tr_isPriority( tmp ) )
993                tr_torrentSetPriority( tor, tmp );
994        if( !errmsg && tr_bencDictFindList( args_in, "files-unwanted", &files ) )
995            errmsg = setFileDLs( tor, FALSE, files );
996        if( !errmsg && tr_bencDictFindList( args_in, "files-wanted", &files ) )
997            errmsg = setFileDLs( tor, TRUE, files );
998        if( tr_bencDictFindInt( args_in, "peer-limit", &tmp ) )
999            tr_torrentSetPeerLimit( tor, tmp );
1000        if( !errmsg &&  tr_bencDictFindList( args_in, "priority-high", &files ) )
1001            errmsg = setFilePriorities( tor, TR_PRI_HIGH, files );
1002        if( !errmsg && tr_bencDictFindList( args_in, "priority-low", &files ) )
1003            errmsg = setFilePriorities( tor, TR_PRI_LOW, files );
1004        if( !errmsg && tr_bencDictFindList( args_in, "priority-normal", &files ) )
1005            errmsg = setFilePriorities( tor, TR_PRI_NORMAL, files );
1006        if( tr_bencDictFindInt( args_in, "downloadLimit", &tmp ) )
1007            tr_torrentSetSpeedLimit_Bps( tor, TR_DOWN, tmp );
1008        if( tr_bencDictFindBool( args_in, "downloadLimited", &boolVal ) )
1009            tr_torrentUseSpeedLimit( tor, TR_DOWN, boolVal );
1010        if( tr_bencDictFindBool( args_in, "honorsSessionLimits", &boolVal ) )
1011            tr_torrentUseSessionLimits( tor, boolVal );
1012        if( tr_bencDictFindInt( args_in, "uploadLimit", &tmp ) )
1013            tr_torrentSetSpeedLimit_Bps( tor, TR_UP, tmp );
1014        if( tr_bencDictFindBool( args_in, "uploadLimited", &boolVal ) )
1015            tr_torrentUseSpeedLimit( tor, TR_UP, boolVal );
1016        if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
1017            tr_torrentSetRatioLimit( tor, d );
1018        if( tr_bencDictFindInt( args_in, "seedRatioMode", &tmp ) )
1019            tr_torrentSetRatioMode( tor, tmp );
1020        if( !errmsg && tr_bencDictFindDict( args_in, "trackerAdd", &tracker ) )
1021            errmsg = addTracker( tor, tracker );
1022        if( !errmsg && tr_bencDictFindDict( args_in, "trackerEdit", &tracker ) )
1023            errmsg = editTracker( tor, tracker );
1024        if( !errmsg && tr_bencDictFindDict( args_in, "trackerRemove", &tracker ) )
1025            errmsg = removeTracker( tor, tracker );
1026        notify( session, TR_RPC_TORRENT_CHANGED, tor );
1027    }
1028
1029    tr_free( torrents );
1030    return errmsg;
1031}
1032
1033static const char*
1034torrentSetLocation( tr_session               * session,
1035                    tr_benc                  * args_in,
1036                    tr_benc                  * args_out UNUSED,
1037                    struct tr_rpc_idle_data  * idle_data UNUSED )
1038{
1039    const char * errmsg = NULL;
1040    const char * location = NULL;
1041
1042    assert( idle_data == NULL );
1043
1044    if( !tr_bencDictFindStr( args_in, "location", &location ) )
1045    {
1046        errmsg = "no location";
1047    }
1048    else
1049    {
1050        tr_bool move = FALSE;
1051        int i, torrentCount;
1052        tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
1053
1054        tr_bencDictFindBool( args_in, "move", &move );
1055
1056        for( i=0; i<torrentCount; ++i )
1057        {
1058            tr_torrent * tor = torrents[i];
1059            tr_torrentSetLocation( tor, location, move, NULL, NULL );
1060            notify( session, TR_RPC_TORRENT_MOVED, tor );
1061        }
1062
1063        tr_free( torrents );
1064    }
1065
1066    return errmsg;
1067}
1068
1069/***
1070****
1071***/
1072
1073static void
1074portTested( tr_session       * session UNUSED,
1075            long               response_code,
1076            const void       * response,
1077            size_t             response_byte_count,
1078            void             * user_data )
1079{
1080    char result[1024];
1081    struct tr_rpc_idle_data * data = user_data;
1082
1083    if( response_code != 200 )
1084    {
1085        tr_snprintf( result, sizeof( result ), "portTested: http error %ld: %s",
1086                     response_code, tr_webGetResponseStr( response_code ) );
1087    }
1088    else /* success */
1089    {
1090        const tr_bool isOpen = response_byte_count && *(char*)response == '1';
1091        tr_bencDictAddBool( data->args_out, "port-is-open", isOpen );
1092        tr_snprintf( result, sizeof( result ), "success" );
1093    }
1094
1095    tr_idle_function_done( data, result );
1096}
1097
1098static const char*
1099portTest( tr_session               * session,
1100          tr_benc                  * args_in UNUSED,
1101          tr_benc                  * args_out UNUSED,
1102          struct tr_rpc_idle_data  * idle_data )
1103{
1104    const int port = tr_sessionGetPeerPort( session );
1105    char * url = tr_strdup_printf( "http://portcheck.transmissionbt.com/%d", port );
1106    tr_webRun( session, url, NULL, portTested, idle_data );
1107    tr_free( url );
1108    return NULL;
1109}
1110
1111/***
1112****
1113***/
1114
1115static void
1116gotNewBlocklist( tr_session       * session,
1117                 long               response_code,
1118                 const void       * response,
1119                 size_t             response_byte_count,
1120                 void             * user_data )
1121{
1122    char result[1024];
1123    struct tr_rpc_idle_data * data = user_data;
1124
1125    if( response_code != 200 )
1126    {
1127        tr_snprintf( result, sizeof( result ), "gotNewBlocklist: http error %ld: %s",
1128                     response_code, tr_webGetResponseStr( response_code ) );
1129    }
1130    else /* successfully fetched the blocklist... */
1131    {
1132        const char * configDir = tr_sessionGetConfigDir( session );
1133        char * filename = tr_buildPath( configDir, "blocklist.tmp", NULL );
1134        FILE * fp = fopen( filename, "wb+" );
1135
1136        if( fp == NULL )
1137        {
1138            tr_snprintf( result, sizeof( result ),
1139                         _( "Couldn't save file \"%1$s\": %2$s" ),
1140                         filename, tr_strerror( errno ) );
1141        }
1142        else
1143        {
1144            const size_t n = fwrite( response, 1, response_byte_count, fp );
1145            fclose( fp );
1146
1147            if( n != response_byte_count )
1148            {
1149                tr_snprintf( result, sizeof( result ),
1150                             _( "Couldn't save file \"%1$s\": %2$s" ),
1151                             filename, tr_strerror( errno ) );
1152            }
1153            else
1154            {
1155                /* feed it to the session */
1156                const int ruleCount = tr_blocklistSetContent( session, filename );
1157
1158                /* give the client a response */
1159                tr_bencDictAddInt( data->args_out, "blocklist-size", ruleCount );
1160                tr_snprintf( result, sizeof( result ), "success" );
1161            }
1162
1163            /* cleanup */
1164            unlink( filename );
1165        }
1166
1167        /* cleanup */
1168        tr_free( filename );
1169    }
1170
1171    tr_idle_function_done( data, result );
1172}
1173
1174static const char*
1175blocklistUpdate( tr_session               * session,
1176                 tr_benc                  * args_in UNUSED,
1177                 tr_benc                  * args_out UNUSED,
1178                 struct tr_rpc_idle_data  * idle_data )
1179{
1180    const char * url = "http://update.transmissionbt.com/level1";
1181    tr_webRun( session, url, NULL, gotNewBlocklist, idle_data );
1182    return NULL;
1183}
1184
1185/***
1186****
1187***/
1188
1189static void
1190addTorrentImpl( struct tr_rpc_idle_data * data, tr_ctor * ctor )
1191{
1192    int err = 0;
1193    const char * result = NULL;
1194    tr_torrent * tor = tr_torrentNew( ctor, &err );
1195
1196    tr_ctorFree( ctor );
1197
1198    if( tor )
1199    {
1200        tr_benc fields;
1201        tr_bencInitList( &fields, 3 );
1202        tr_bencListAddStr( &fields, "id" );
1203        tr_bencListAddStr( &fields, "name" );
1204        tr_bencListAddStr( &fields, "hashString" );
1205        addInfo( tor, tr_bencDictAdd( data->args_out, "torrent-added" ), &fields );
1206        notify( data->session, TR_RPC_TORRENT_ADDED, tor );
1207        tr_bencFree( &fields );
1208    }
1209    else if( err == TR_PARSE_DUPLICATE )
1210    {
1211        result = "duplicate torrent";
1212    }
1213    else if( err == TR_PARSE_ERR )
1214    {
1215        result = "invalid or corrupt torrent file";
1216    }
1217
1218    tr_idle_function_done( data, result );
1219}
1220
1221
1222struct add_torrent_idle_data
1223{
1224    struct tr_rpc_idle_data * data;
1225    tr_ctor * ctor;
1226};
1227
1228static void
1229gotMetadataFromURL( tr_session       * session UNUSED,
1230                    long               response_code,
1231                    const void       * response,
1232                    size_t             response_byte_count,
1233                    void             * user_data )
1234{
1235    struct add_torrent_idle_data * data = user_data;
1236
1237    dbgmsg( "torrentAdd: HTTP response code was %ld (%s); response length was %zu bytes",
1238            response_code, tr_webGetResponseStr( response_code ), response_byte_count );
1239
1240    if( response_code==200 || response_code==221 ) /* http or ftp success.. */
1241    {
1242        tr_ctorSetMetainfo( data->ctor, response, response_byte_count );
1243        addTorrentImpl( data->data, data->ctor );
1244    }
1245    else
1246    {
1247        char result[1024];
1248        tr_snprintf( result, sizeof( result ), "gotMetadataFromURL: http error %ld: %s",
1249                     response_code, tr_webGetResponseStr( response_code ) );
1250        tr_idle_function_done( data->data, result );
1251    }
1252
1253    tr_free( data );
1254}
1255
1256static tr_bool
1257isCurlURL( const char * filename )
1258{
1259    if( filename == NULL )
1260        return FALSE;
1261
1262    return !strncmp( filename, "ftp://", 6 ) ||
1263           !strncmp( filename, "http://", 7 ) ||
1264           !strncmp( filename, "https://", 8 );
1265}
1266
1267static tr_file_index_t*
1268fileListFromList( tr_benc * list, tr_file_index_t * setmeCount )
1269{
1270    size_t i;
1271    const size_t childCount = tr_bencListSize( list );
1272    tr_file_index_t n = 0;
1273    tr_file_index_t * files = tr_new0( tr_file_index_t, childCount );
1274
1275    for( i=0; i<childCount; ++i ) {
1276        int64_t intVal;
1277        if( tr_bencGetInt( tr_bencListChild( list, i ), &intVal ) )
1278            files[n++] = (tr_file_index_t)intVal;
1279    }
1280
1281    *setmeCount = n;
1282    return files;
1283}
1284
1285static const char*
1286torrentAdd( tr_session               * session,
1287            tr_benc                  * args_in,
1288            tr_benc                  * args_out UNUSED,
1289            struct tr_rpc_idle_data  * idle_data )
1290{
1291    const char * filename = NULL;
1292    const char * metainfo_base64 = NULL;
1293
1294    assert( idle_data != NULL );
1295
1296    tr_bencDictFindStr( args_in, "filename", &filename );
1297    tr_bencDictFindStr( args_in, "metainfo", &metainfo_base64 );
1298    if( !filename && !metainfo_base64 )
1299        return "no filename or metainfo specified";
1300    else
1301    {
1302        int64_t      i;
1303        tr_bool      boolVal;
1304        const char * str;
1305        tr_benc    * l;
1306        tr_ctor    * ctor = tr_ctorNew( session );
1307
1308        /* set the optional arguments */
1309
1310        if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1311            tr_ctorSetDownloadDir( ctor, TR_FORCE, str );
1312
1313        if( tr_bencDictFindBool( args_in, "paused", &boolVal ) )
1314            tr_ctorSetPaused( ctor, TR_FORCE, boolVal );
1315
1316        if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
1317            tr_ctorSetPeerLimit( ctor, TR_FORCE, i );
1318
1319        if( tr_bencDictFindInt( args_in, "bandwidthPriority", &i ) )
1320            tr_ctorSetBandwidthPriority( ctor, i );
1321
1322        if( tr_bencDictFindList( args_in, "files-unwanted", &l ) ) {
1323            tr_file_index_t fileCount;
1324            tr_file_index_t * files = fileListFromList( l, &fileCount );
1325            tr_ctorSetFilesWanted( ctor, files, fileCount, FALSE );
1326            tr_free( files );
1327        }
1328        if( tr_bencDictFindList( args_in, "files-wanted", &l ) ) {
1329            tr_file_index_t fileCount;
1330            tr_file_index_t * files = fileListFromList( l, &fileCount );
1331            tr_ctorSetFilesWanted( ctor, files, fileCount, TRUE );
1332            tr_free( files );
1333        }
1334
1335        if( tr_bencDictFindList( args_in, "priority-low", &l ) ) {
1336            tr_file_index_t fileCount;
1337            tr_file_index_t * files = fileListFromList( l, &fileCount );
1338            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_LOW );
1339            tr_free( files );
1340        }
1341        if( tr_bencDictFindList( args_in, "priority-normal", &l ) ) {
1342            tr_file_index_t fileCount;
1343            tr_file_index_t * files = fileListFromList( l, &fileCount );
1344            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_NORMAL );
1345            tr_free( files );
1346        }
1347        if( tr_bencDictFindList( args_in, "priority-high", &l ) ) {
1348            tr_file_index_t fileCount;
1349            tr_file_index_t * files = fileListFromList( l, &fileCount );
1350            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_HIGH );
1351            tr_free( files );
1352        }
1353
1354        dbgmsg( "torrentAdd: filename is \"%s\"", filename ? filename : "(null)" );
1355
1356        if( isCurlURL( filename ) )
1357        {
1358            struct add_torrent_idle_data * d = tr_new0( struct add_torrent_idle_data, 1 );
1359            d->data = idle_data;
1360            d->ctor = ctor;
1361            tr_webRun( session, filename, NULL, gotMetadataFromURL, d );
1362        }
1363        else
1364        {
1365            char * fname = tr_strstrip( tr_strdup( filename ) );
1366
1367            if( fname == NULL )
1368            {
1369                int len;
1370                char * metainfo = tr_base64_decode( metainfo_base64, -1, &len );
1371                tr_ctorSetMetainfo( ctor, (uint8_t*)metainfo, len );
1372                tr_free( metainfo );
1373            }
1374            else if( !strncmp( fname, "magnet:?", 8 ) )
1375            {
1376                tr_ctorSetMetainfoFromMagnetLink( ctor, fname );
1377            }
1378            else
1379            {
1380                tr_ctorSetMetainfoFromFile( ctor, fname );
1381            }
1382
1383            addTorrentImpl( idle_data, ctor );
1384
1385            tr_free( fname );
1386        }
1387
1388    }
1389
1390    return NULL;
1391}
1392
1393/***
1394****
1395***/
1396
1397static const char*
1398sessionSet( tr_session               * session,
1399            tr_benc                  * args_in,
1400            tr_benc                  * args_out UNUSED,
1401            struct tr_rpc_idle_data  * idle_data UNUSED )
1402{
1403    int64_t      i;
1404    double       d;
1405    tr_bool      boolVal;
1406    const char * str;
1407
1408    assert( idle_data == NULL );
1409
1410    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_MAX_CACHE_SIZE, &i ) )
1411        tr_sessionSetCacheLimit( session, d );
1412    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_UP_Bps, &i ) )
1413        tr_sessionSetAltSpeed_Bps( session, TR_UP, i );
1414    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_DOWN_Bps, &i ) )
1415        tr_sessionSetAltSpeed_Bps( session, TR_DOWN, i );
1416    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_ENABLED, &boolVal ) )
1417        tr_sessionUseAltSpeed( session, boolVal );
1418    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &i ) )
1419        tr_sessionSetAltSpeedBegin( session, i );
1420    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_END, &i ) )
1421        tr_sessionSetAltSpeedEnd( session, i );
1422    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &i ) )
1423        tr_sessionSetAltSpeedDay( session, i );
1424    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &boolVal ) )
1425        tr_sessionUseAltSpeedTime( session, boolVal );
1426    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_BLOCKLIST_ENABLED, &boolVal ) )
1427        tr_blocklistSetEnabled( session, boolVal );
1428    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1429        tr_sessionSetDownloadDir( session, str );
1430    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_INCOMPLETE_DIR, &str ) )
1431        tr_sessionSetIncompleteDir( session, str );
1432    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, &boolVal ) )
1433        tr_sessionSetIncompleteDirEnabled( session, boolVal );
1434    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &i ) )
1435        tr_sessionSetPeerLimit( session, i );
1436    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i ) )
1437        tr_sessionSetPeerLimitPerTorrent( session, i );
1438    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) )
1439        tr_sessionSetPexEnabled( session, boolVal );
1440    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DHT_ENABLED, &boolVal ) )
1441        tr_sessionSetDHTEnabled( session, boolVal );
1442    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_LPD_ENABLED, &boolVal ) )
1443        tr_sessionSetLPDEnabled( session, boolVal );
1444    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, &boolVal ) )
1445        tr_sessionSetPeerPortRandomOnStart( session, boolVal );
1446    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_PORT, &i ) )
1447        tr_sessionSetPeerPort( session, i );
1448    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) )
1449        tr_sessionSetPortForwardingEnabled( session, boolVal );
1450    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_RENAME_PARTIAL_FILES, &boolVal ) )
1451        tr_sessionSetIncompleteFileNamingEnabled( session, boolVal );
1452    if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
1453        tr_sessionSetRatioLimit( session, d );
1454    if( tr_bencDictFindBool( args_in, "seedRatioLimited", &boolVal ) )
1455        tr_sessionSetRatioLimited( session, boolVal );
1456    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_START, &boolVal ) )
1457        tr_sessionSetPaused( session, !boolVal );
1458    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, &str ) )
1459        tr_sessionSetTorrentDoneScript( session, str );
1460    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, &boolVal ) )
1461        tr_sessionSetTorrentDoneScriptEnabled( session, boolVal );
1462    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_TRASH_ORIGINAL, &boolVal ) )
1463        tr_sessionSetDeleteSource( session, boolVal );
1464    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_DSPEED_Bps, &i ) )
1465        tr_sessionSetSpeedLimit_Bps( session, TR_DOWN, i );
1466    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DSPEED_ENABLED, &boolVal ) )
1467        tr_sessionLimitSpeed( session, TR_DOWN, boolVal );
1468    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_USPEED_Bps, &i ) )
1469        tr_sessionSetSpeedLimit_Bps( session, TR_UP, i );
1470    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_USPEED_ENABLED, &boolVal ) )
1471        tr_sessionLimitSpeed( session, TR_UP, boolVal );
1472    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_ENCRYPTION, &str ) ) {
1473        if( !strcmp( str, "required" ) )
1474            tr_sessionSetEncryption( session, TR_ENCRYPTION_REQUIRED );
1475        else if( !strcmp( str, "tolerated" ) )
1476            tr_sessionSetEncryption( session, TR_CLEAR_PREFERRED );
1477        else
1478            tr_sessionSetEncryption( session, TR_ENCRYPTION_PREFERRED );
1479    }
1480
1481    notify( session, TR_RPC_SESSION_CHANGED, NULL );
1482
1483    return NULL;
1484}
1485
1486static const char*
1487sessionStats( tr_session               * session,
1488              tr_benc                  * args_in UNUSED,
1489              tr_benc                  * args_out,
1490              struct tr_rpc_idle_data  * idle_data UNUSED )
1491{
1492    int running = 0;
1493    int total = 0;
1494    tr_benc * d;
1495    tr_session_stats currentStats = { 0.0f, 0, 0, 0, 0, 0 };
1496    tr_session_stats cumulativeStats = { 0.0f, 0, 0, 0, 0, 0 };
1497    tr_torrent * tor = NULL;
1498
1499    assert( idle_data == NULL );
1500
1501    while(( tor = tr_torrentNext( session, tor ))) {
1502        ++total;
1503        if( tor->isRunning )
1504            ++running;
1505    }
1506
1507    tr_sessionGetStats( session, &currentStats );
1508    tr_sessionGetCumulativeStats( session, &cumulativeStats );
1509
1510    tr_bencDictAddInt( args_out, "activeTorrentCount", running );
1511    tr_bencDictAddInt( args_out, "downloadSpeed", tr_sessionGetPieceSpeed_Bps( session, TR_DOWN ) );
1512    tr_bencDictAddInt( args_out, "pausedTorrentCount", total - running );
1513    tr_bencDictAddInt( args_out, "torrentCount", total );
1514    tr_bencDictAddInt( args_out, "uploadSpeed", tr_sessionGetPieceSpeed_Bps( session, TR_UP ) );
1515
1516    d = tr_bencDictAddDict( args_out, "cumulative-stats", 5 );
1517    tr_bencDictAddInt( d, "downloadedBytes", cumulativeStats.downloadedBytes );
1518    tr_bencDictAddInt( d, "filesAdded", cumulativeStats.filesAdded );
1519    tr_bencDictAddInt( d, "secondsActive", cumulativeStats.secondsActive );
1520    tr_bencDictAddInt( d, "sessionCount", cumulativeStats.sessionCount );
1521    tr_bencDictAddInt( d, "uploadedBytes", cumulativeStats.uploadedBytes );
1522
1523    d = tr_bencDictAddDict( args_out, "current-stats", 5 );
1524    tr_bencDictAddInt( d, "downloadedBytes", currentStats.downloadedBytes );
1525    tr_bencDictAddInt( d, "filesAdded", currentStats.filesAdded );
1526    tr_bencDictAddInt( d, "secondsActive", currentStats.secondsActive );
1527    tr_bencDictAddInt( d, "sessionCount", currentStats.sessionCount );
1528    tr_bencDictAddInt( d, "uploadedBytes", currentStats.uploadedBytes );
1529
1530    return NULL;
1531}
1532
1533static const char*
1534sessionGet( tr_session               * s,
1535            tr_benc                  * args_in UNUSED,
1536            tr_benc                  * args_out,
1537            struct tr_rpc_idle_data  * idle_data UNUSED )
1538{
1539    const char * str;
1540    tr_benc *    d = args_out;
1541
1542    assert( idle_data == NULL );
1543    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP_Bps, tr_sessionGetAltSpeed_Bps(s,TR_UP) );
1544    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN_Bps, tr_sessionGetAltSpeed_Bps(s,TR_DOWN) );
1545    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED, tr_sessionUsesAltSpeed(s) );
1546    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, tr_sessionGetAltSpeedBegin(s) );
1547    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,tr_sessionGetAltSpeedEnd(s) );
1548    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,tr_sessionGetAltSpeedDay(s) );
1549    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, tr_sessionUsesAltSpeedTime(s) );
1550    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, tr_blocklistIsEnabled( s ) );
1551    tr_bencDictAddReal( d, TR_PREFS_KEY_MAX_CACHE_SIZE, tr_sessionGetCacheLimit( s ) );
1552    tr_bencDictAddInt ( d, "blocklist-size", tr_blocklistGetRuleCount( s ) );
1553    tr_bencDictAddStr ( d, "config-dir", tr_sessionGetConfigDir( s ) );
1554    tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR, tr_sessionGetDownloadDir( s ) );
1555    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, tr_sessionGetPeerLimit( s ) );
1556    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT, tr_sessionGetPeerLimitPerTorrent( s ) );
1557    tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR, tr_sessionGetIncompleteDir( s ) );
1558    tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, tr_sessionIsIncompleteDirEnabled( s ) );
1559    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED, tr_sessionIsPexEnabled( s ) );
1560    tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED, tr_sessionIsDHTEnabled( s ) );
1561    tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED, tr_sessionIsLPDEnabled( s ) );
1562    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT, tr_sessionGetPeerPort( s ) );
1563    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, tr_sessionGetPeerPortRandomOnStart( s ) );
1564    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) );
1565    tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES, tr_sessionIsIncompleteFileNamingEnabled( s ) );
1566    tr_bencDictAddInt ( d, "rpc-version", RPC_VERSION );
1567    tr_bencDictAddInt ( d, "rpc-version-minimum", RPC_VERSION_MIN );
1568    tr_bencDictAddReal( d, "seedRatioLimit", tr_sessionGetRatioLimit( s ) );
1569    tr_bencDictAddBool( d, "seedRatioLimited", tr_sessionIsRatioLimited( s ) );
1570    tr_bencDictAddBool( d, TR_PREFS_KEY_START, !tr_sessionGetPaused( s ) );
1571    tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL, tr_sessionGetDeleteSource( s ) );
1572    tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_Bps, tr_sessionGetSpeedLimit_Bps( s, TR_UP ) );
1573    tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_UP ) );
1574    tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_Bps, tr_sessionGetSpeedLimit_Bps( s, TR_DOWN ) );
1575    tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_DOWN ) );
1576    tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, tr_sessionGetTorrentDoneScript( s ) );
1577    tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, tr_sessionIsTorrentDoneScriptEnabled( s ) );
1578    tr_bencDictAddStr ( d, "version", LONG_VERSION_STRING );
1579    switch( tr_sessionGetEncryption( s ) ) {
1580        case TR_CLEAR_PREFERRED: str = "tolerated"; break;
1581        case TR_ENCRYPTION_REQUIRED: str = "required"; break;
1582        default: str = "preferred"; break;
1583    }
1584    tr_bencDictAddStr( d, TR_PREFS_KEY_ENCRYPTION, str );
1585
1586    return NULL;
1587}
1588
1589/***
1590****
1591***/
1592
1593typedef const char* ( *handler )( tr_session*, tr_benc*, tr_benc*, struct tr_rpc_idle_data * );
1594
1595static struct method
1596{
1597    const char *  name;
1598    tr_bool       immediate;
1599    handler       func;
1600}
1601methods[] =
1602{
1603    { "port-test",             FALSE, portTest            },
1604    { "blocklist-update",      FALSE, blocklistUpdate     },
1605    { "session-get",           TRUE,  sessionGet          },
1606    { "session-set",           TRUE,  sessionSet          },
1607    { "session-stats",         TRUE,  sessionStats        },
1608    { "torrent-add",           FALSE, torrentAdd          },
1609    { "torrent-get",           TRUE,  torrentGet          },
1610    { "torrent-remove",        TRUE,  torrentRemove       },
1611    { "torrent-set",           TRUE,  torrentSet          },
1612    { "torrent-set-location",  TRUE,  torrentSetLocation  },
1613    { "torrent-start",         TRUE,  torrentStart        },
1614    { "torrent-stop",          TRUE,  torrentStop         },
1615    { "torrent-verify",        TRUE,  torrentVerify       },
1616    { "torrent-reannounce",    TRUE,  torrentReannounce   }
1617};
1618
1619static void
1620noop_response_callback( tr_session * session UNUSED,
1621                        const char * response UNUSED,
1622                        size_t       response_len UNUSED,
1623                        void       * user_data UNUSED )
1624{
1625}
1626
1627static void
1628request_exec( tr_session             * session,
1629              tr_benc                * request,
1630              tr_rpc_response_func     callback,
1631              void                   * callback_user_data )
1632{
1633    int i;
1634    const char * str;
1635    tr_benc * args_in = tr_bencDictFind( request, "arguments" );
1636    const char * result = NULL;
1637
1638    if( callback == NULL )
1639        callback = noop_response_callback;
1640
1641    /* parse the request */
1642    if( !tr_bencDictFindStr( request, "method", &str ) )
1643        result = "no method name";
1644    else {
1645        const int n = TR_N_ELEMENTS( methods );
1646        for( i = 0; i < n; ++i )
1647            if( !strcmp( str, methods[i].name ) )
1648                break;
1649        if( i ==n )
1650            result = "method name not recognized";
1651    }
1652
1653    /* if we couldn't figure out which method to use, return an error */
1654    if( result != NULL )
1655    {
1656        int64_t tag;
1657        tr_benc response;
1658        struct evbuffer * buf = evbuffer_new( );
1659
1660        tr_bencInitDict( &response, 3 );
1661        tr_bencDictAddDict( &response, "arguments", 0 );
1662        tr_bencDictAddStr( &response, "result", result );
1663        if( tr_bencDictFindInt( request, "tag", &tag ) )
1664            tr_bencDictAddInt( &response, "tag", tag );
1665        tr_bencToBuf( &response, TR_FMT_JSON_LEAN, buf );
1666        (*callback)( session, (const char*)EVBUFFER_DATA(buf),
1667                     EVBUFFER_LENGTH( buf ), callback_user_data );
1668
1669        evbuffer_free( buf );
1670        tr_bencFree( &response );
1671    }
1672    else if( methods[i].immediate )
1673    {
1674        int64_t tag;
1675        tr_benc response;
1676        tr_benc * args_out;
1677        struct evbuffer * buf = evbuffer_new( );
1678
1679        tr_bencInitDict( &response, 3 );
1680        args_out = tr_bencDictAddDict( &response, "arguments", 0 );
1681        result = (*methods[i].func)( session, args_in, args_out, NULL );
1682        if( result == NULL )
1683            result = "success";
1684        tr_bencDictAddStr( &response, "result", result );
1685        if( tr_bencDictFindInt( request, "tag", &tag ) )
1686            tr_bencDictAddInt( &response, "tag", tag );
1687        tr_bencToBuf( &response, TR_FMT_JSON_LEAN, buf );
1688        (*callback)( session, (const char*)EVBUFFER_DATA(buf),
1689                     EVBUFFER_LENGTH(buf), callback_user_data );
1690
1691        evbuffer_free( buf );
1692        tr_bencFree( &response );
1693    }
1694    else
1695    {
1696        int64_t tag;
1697        struct tr_rpc_idle_data * data = tr_new0( struct tr_rpc_idle_data, 1 );
1698        data->session = session;
1699        data->response = tr_new0( tr_benc, 1 );
1700        tr_bencInitDict( data->response, 3 );
1701        if( tr_bencDictFindInt( request, "tag", &tag ) )
1702            tr_bencDictAddInt( data->response, "tag", tag );
1703        data->args_out = tr_bencDictAddDict( data->response, "arguments", 0 );
1704        data->callback = callback;
1705        data->callback_user_data = callback_user_data;
1706        (*methods[i].func)( session, args_in, data->args_out, data );
1707    }
1708}
1709
1710void
1711tr_rpc_request_exec_json( tr_session            * session,
1712                          const void            * request_json,
1713                          int                     request_len,
1714                          tr_rpc_response_func    callback,
1715                          void                  * callback_user_data )
1716{
1717    tr_benc top;
1718    int have_content;
1719
1720    if( request_len < 0 )
1721        request_len = strlen( request_json );
1722
1723    have_content = !tr_jsonParse( "rpc", request_json, request_len, &top, NULL );
1724    request_exec( session, have_content ? &top : NULL, callback, callback_user_data );
1725
1726    if( have_content )
1727        tr_bencFree( &top );
1728}
1729
1730/**
1731 * Munge the URI into a usable form.
1732 *
1733 * We have very loose typing on this to make the URIs as simple as possible:
1734 * - anything not a 'tag' or 'method' is automatically in 'arguments'
1735 * - values that are all-digits are numbers
1736 * - values that are all-digits or commas are number lists
1737 * - all other values are strings
1738 */
1739void
1740tr_rpc_parse_list_str( tr_benc     * setme,
1741                       const char  * str,
1742                       int           len )
1743
1744{
1745    int valueCount;
1746    int * values = tr_parseNumberRange( str, len, &valueCount );
1747
1748    if( valueCount == 0 )
1749        tr_bencInitStr( setme, str, len );
1750    else if( valueCount == 1 )
1751        tr_bencInitInt( setme, values[0] );
1752    else {
1753        int i;
1754        tr_bencInitList( setme, valueCount );
1755        for( i=0; i<valueCount; ++i )
1756            tr_bencListAddInt( setme, values[i] );
1757    }
1758
1759    tr_free( values );
1760}
1761
1762void
1763tr_rpc_request_exec_uri( tr_session           * session,
1764                         const void           * request_uri,
1765                         int                    request_len,
1766                         tr_rpc_response_func   callback,
1767                         void                 * callback_user_data )
1768{
1769    tr_benc      top, * args;
1770    char *       request = tr_strndup( request_uri, request_len );
1771    const char * pch;
1772
1773    tr_bencInitDict( &top, 3 );
1774    args = tr_bencDictAddDict( &top, "arguments", 0 );
1775
1776    pch = strchr( request, '?' );
1777    if( !pch ) pch = request;
1778    while( pch )
1779    {
1780        const char * delim = strchr( pch, '=' );
1781        const char * next = strchr( pch, '&' );
1782        if( delim )
1783        {
1784            char *    key = tr_strndup( pch, delim - pch );
1785            int       isArg = strcmp( key, "method" ) && strcmp( key, "tag" );
1786            tr_benc * parent = isArg ? args : &top;
1787            tr_rpc_parse_list_str( tr_bencDictAdd( parent, key ),
1788                                  delim + 1,
1789                                  next ? (size_t)(
1790                                       next -
1791                                      ( delim + 1 ) ) : strlen( delim + 1 ) );
1792            tr_free( key );
1793        }
1794        pch = next ? next + 1 : NULL;
1795    }
1796
1797    request_exec( session, &top, callback, callback_user_data );
1798
1799    /* cleanup */
1800    tr_bencFree( &top );
1801    tr_free( request );
1802}
Note: See TracBrowser for help on using the repository browser.