source: trunk/libtransmission/rpcimpl.c @ 12641

Last change on this file since 12641 was 12641, checked in by jordan, 10 years ago

bump RPC_VERSION to 14 because of the queuing changes

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