source: trunk/libtransmission/rpcimpl.c @ 12391

Last change on this file since 12391 was 12391, checked in by jordan, 11 years ago

(trunk libT) tweak the ABI of tr_bencToBuf() to match the way it's being used

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