source: trunk/libtransmission/rpcimpl.c @ 11194

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

(trunk) revert the RPC behavior of rateUpload, rateDownload, rateToClient, and rateToPeer as discussed with Longinus00

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