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