source: trunk/libtransmission/rpcimpl.c @ 12228

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

(trunk libT) still fiddling around with #includes -- this time removing unncecessary libT includes from libT .c files

  • Property svn:keywords set to Date Rev Author Id
File size: 65.8 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 12228 2011-03-25 01:41:57Z 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 = evbuffer_new( );
98
99    if( result == NULL )
100        result = "success";
101    tr_bencDictAddStr( data->response, "result", result );
102
103    tr_bencToBuf( data->response, TR_FMT_JSON_LEAN, buf );
104    (*data->callback)( data->session, buf, data->callback_user_data );
105
106    evbuffer_free( buf );
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        tr_bitfield * bf = tr_cpCreatePieceBitfield( &tor->completion );
562        char * str = tr_base64_encode( bf->bits, bf->byteCount, NULL );
563        tr_bencDictAddStr( d, key, str!=NULL ? str : "" );
564        tr_free( str );
565        tr_bitfieldFree( bf );
566    }
567    else if( tr_streq( key, keylen, "pieceCount" ) )
568        tr_bencDictAddInt( d, key, inf->pieceCount );
569    else if( tr_streq( key, keylen, "pieceSize" ) )
570        tr_bencDictAddInt( d, key, inf->pieceSize );
571    else if( tr_streq( key, keylen, "priorities" ) )
572    {
573        tr_file_index_t i;
574        tr_benc *       p = tr_bencDictAddList( d, key, inf->fileCount );
575        for( i = 0; i < inf->fileCount; ++i )
576            tr_bencListAddInt( p, inf->files[i].priority );
577    }
578    else if( tr_streq( key, keylen, "rateDownload" ) )
579        tr_bencDictAddInt( d, key, toSpeedBytes( st->pieceDownloadSpeed_KBps ) );
580    else if( tr_streq( key, keylen, "rateUpload" ) )
581        tr_bencDictAddInt( d, key, toSpeedBytes( st->pieceUploadSpeed_KBps ) );
582    else if( tr_streq( key, keylen, "recheckProgress" ) )
583        tr_bencDictAddReal( d, key, st->recheckProgress );
584    else if( tr_streq( key, keylen, "seedIdleLimit" ) )
585        tr_bencDictAddInt( d, key, tr_torrentGetIdleLimit( tor ) );
586    else if( tr_streq( key, keylen, "seedIdleMode" ) )
587        tr_bencDictAddInt( d, key, tr_torrentGetIdleMode( tor ) );
588    else if( tr_streq( key, keylen, "seedRatioLimit" ) )
589        tr_bencDictAddReal( d, key, tr_torrentGetRatioLimit( tor ) );
590    else if( tr_streq( key, keylen, "seedRatioMode" ) )
591        tr_bencDictAddInt( d, key, tr_torrentGetRatioMode( tor ) );
592    else if( tr_streq( key, keylen, "sizeWhenDone" ) )
593        tr_bencDictAddInt( d, key, st->sizeWhenDone );
594    else if( tr_streq( key, keylen, "startDate" ) )
595        tr_bencDictAddInt( d, key, st->startDate );
596    else if( tr_streq( key, keylen, "status" ) )
597        tr_bencDictAddInt( d, key, st->activity );
598    else if( tr_streq( key, keylen, "secondsDownloading" ) )
599        tr_bencDictAddInt( d, key, st->secondsDownloading );
600    else if( tr_streq( key, keylen, "secondsSeeding" ) )
601        tr_bencDictAddInt( d, key, st->secondsSeeding );
602    else if( tr_streq( key, keylen, "trackers" ) )
603        addTrackers( inf, tr_bencDictAddList( d, key, inf->trackerCount ) );
604    else if( tr_streq( key, keylen, "trackerStats" ) ) {
605        int n;
606        tr_tracker_stat * s = tr_torrentTrackers( tor, &n );
607        addTrackerStats( s, n, tr_bencDictAddList( d, key, n ) );
608        tr_torrentTrackersFree( s, n );
609    }
610    else if( tr_streq( key, keylen, "torrentFile" ) )
611        tr_bencDictAddStr( d, key, inf->torrent );
612    else if( tr_streq( key, keylen, "totalSize" ) )
613        tr_bencDictAddInt( d, key, inf->totalSize );
614    else if( tr_streq( key, keylen, "uploadedEver" ) )
615        tr_bencDictAddInt( d, key, st->uploadedEver );
616    else if( tr_streq( key, keylen, "uploadLimit" ) )
617        tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit_KBps( tor, TR_UP ) );
618    else if( tr_streq( key, keylen, "uploadLimited" ) )
619        tr_bencDictAddBool( d, key, tr_torrentUsesSpeedLimit( tor, TR_UP ) );
620    else if( tr_streq( key, keylen, "uploadRatio" ) )
621        tr_bencDictAddReal( d, key, st->ratio );
622    else if( tr_streq( key, keylen, "wanted" ) )
623    {
624        tr_file_index_t i;
625        tr_benc *       w = tr_bencDictAddList( d, key, inf->fileCount );
626        for( i = 0; i < inf->fileCount; ++i )
627            tr_bencListAddInt( w, inf->files[i].dnd ? 0 : 1 );
628    }
629    else if( tr_streq( key, keylen, "webseeds" ) )
630        addWebseeds( inf, tr_bencDictAddList( d, key, inf->webseedCount ) );
631    else if( tr_streq( key, keylen, "webseedsSendingToUs" ) )
632        tr_bencDictAddInt( d, key, st->webseedsSendingToUs );
633}
634
635static void
636addInfo( const tr_torrent * tor,
637         tr_benc *          d,
638         tr_benc *          fields )
639{
640    int          i;
641    const int    n = tr_bencListSize( fields );
642    const char * str;
643
644    tr_bencInitDict( d, n );
645
646    for( i = 0; i < n; ++i )
647        if( tr_bencGetStr( tr_bencListChild( fields, i ), &str ) )
648            addField( tor, d, str );
649}
650
651static const char*
652torrentGet( tr_session               * session,
653            tr_benc                  * args_in,
654            tr_benc                  * args_out,
655            struct tr_rpc_idle_data  * idle_data UNUSED )
656{
657    int           i, torrentCount;
658    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
659    tr_benc *     list = tr_bencDictAddList( args_out, "torrents", torrentCount );
660    tr_benc *     fields;
661    const char *  msg = NULL;
662    const char *  strVal;
663
664    assert( idle_data == NULL );
665
666    if( tr_bencDictFindStr( args_in, "ids", &strVal ) && !strcmp( strVal, "recently-active" ) ) {
667        int n = 0;
668        tr_benc * d;
669        const time_t now = tr_time( );
670        const int interval = RECENTLY_ACTIVE_SECONDS;
671        tr_benc * removed_out = tr_bencDictAddList( args_out, "removed", 0 );
672        while(( d = tr_bencListChild( &session->removedTorrents, n++ ))) {
673            int64_t intVal;
674            if( tr_bencDictFindInt( d, "date", &intVal ) && ( intVal >= now - interval ) ) {
675                tr_bencDictFindInt( d, "id", &intVal );
676                tr_bencListAddInt( removed_out, intVal );
677            }
678        }
679    }
680
681    if( !tr_bencDictFindList( args_in, "fields", &fields ) )
682        msg = "no fields specified";
683    else for( i = 0; i < torrentCount; ++i )
684        addInfo( torrents[i], tr_bencListAdd( list ), fields );
685
686    tr_free( torrents );
687    return msg;
688}
689
690/***
691****
692***/
693
694static const char*
695setFilePriorities( tr_torrent * tor,
696                   int          priority,
697                   tr_benc *    list )
698{
699    int i;
700    int64_t tmp;
701    int fileCount = 0;
702    const int n = tr_bencListSize( list );
703    const char * errmsg = NULL;
704    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
705
706    if( n )
707    {
708        for( i = 0; i < n; ++i ) {
709            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) {
710                if( 0 <= tmp && tmp < tor->info.fileCount ) {
711                    files[fileCount++] = tmp;
712                } else {
713                    errmsg = "file index out of range";
714                }
715            }
716        }
717    }
718    else /* if empty set, apply to all */
719    {
720        tr_file_index_t t;
721        for( t = 0; t < tor->info.fileCount; ++t )
722            files[fileCount++] = t;
723    }
724
725    if( fileCount )
726        tr_torrentSetFilePriorities( tor, files, fileCount, priority );
727
728    tr_free( files );
729    return errmsg;
730}
731
732static const char*
733setFileDLs( tr_torrent * tor,
734            int          do_download,
735            tr_benc *    list )
736{
737    int i;
738    int64_t tmp;
739    int fileCount = 0;
740    const int n = tr_bencListSize( list );
741    const char * errmsg = NULL;
742    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
743
744    if( n ) /* if argument list, process them */
745    {
746        for( i = 0; i < n; ++i ) {
747            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) {
748                if( 0 <= tmp && tmp < tor->info.fileCount ) {
749                    files[fileCount++] = tmp;
750                } else {
751                    errmsg = "file index out of range";
752                }
753            }
754        }
755    }
756    else /* if empty set, apply to all */
757    {
758        tr_file_index_t t;
759        for( t = 0; t < tor->info.fileCount; ++t )
760            files[fileCount++] = t;
761    }
762
763    if( fileCount )
764        tr_torrentSetFileDLs( tor, files, fileCount, do_download );
765
766    tr_free( files );
767    return errmsg;
768}
769
770static bool
771findAnnounceUrl( const tr_tracker_info * t, int n, const char * url, int * pos )
772{
773    int i;
774    bool found = false;
775
776    for( i=0; i<n; ++i )
777    {
778        if( !strcmp( t[i].announce, url ) )
779        {
780            found = true;
781            if( pos ) *pos = i;
782            break;
783        }
784    }
785
786    return found;
787}
788
789static int
790copyTrackers( tr_tracker_info * tgt, const tr_tracker_info * src, int n )
791{
792    int i;
793    int maxTier = -1;
794
795    for( i=0; i<n; ++i )
796    {
797        tgt[i].tier = src[i].tier;
798        tgt[i].announce = tr_strdup( src[i].announce );
799        maxTier = MAX( maxTier, src[i].tier );
800    }
801
802    return maxTier;
803}
804
805static void
806freeTrackers( tr_tracker_info * trackers, int n )
807{
808    int i;
809
810    for( i=0; i<n; ++i )
811        tr_free( trackers[i].announce );
812
813    tr_free( trackers );
814}
815
816static const char*
817addTrackerUrls( tr_torrent * tor, tr_benc * urls )
818{
819    int i;
820    int n;
821    int tier;
822    tr_benc * val;
823    tr_tracker_info * trackers;
824    bool changed = false;
825    const tr_info * inf = tr_torrentInfo( tor );
826    const char * errmsg = NULL;
827
828    /* make a working copy of the existing announce list */
829    n = inf->trackerCount;
830    trackers = tr_new0( tr_tracker_info, n + tr_bencListSize( urls ) );
831    tier = copyTrackers( trackers, inf->trackers, n );
832
833    /* and add the new ones */
834    i = 0;
835    while(( val = tr_bencListChild( urls, i++ ) ))
836    {
837        const char * announce = NULL;
838
839        if(    tr_bencGetStr( val, &announce )
840            && tr_urlIsValidTracker( announce )
841            && !findAnnounceUrl( trackers, n, announce, NULL ) )
842        {
843            trackers[n].tier = ++tier; /* add a new tier */
844            trackers[n].announce = tr_strdup( announce );
845            ++n;
846            changed = true;
847        }
848    }
849
850    if( !changed )
851        errmsg = "invalid argument";
852    else if( !tr_torrentSetAnnounceList( tor, trackers, n ) )
853        errmsg = "error setting announce list";
854
855    freeTrackers( trackers, n );
856    return errmsg;
857}
858
859static const char*
860replaceTrackers( tr_torrent * tor, tr_benc * urls )
861{
862    int i;
863    tr_benc * pair[2];
864    tr_tracker_info * trackers;
865    bool changed = false;
866    const tr_info * inf = tr_torrentInfo( tor );
867    const int n = inf->trackerCount;
868    const char * errmsg = NULL;
869
870    /* make a working copy of the existing announce list */
871    trackers = tr_new0( tr_tracker_info, n );
872    copyTrackers( trackers, inf->trackers, n );
873
874    /* make the substitutions... */
875    i = 0;
876    while(((pair[0] = tr_bencListChild(urls,i))) &&
877          ((pair[1] = tr_bencListChild(urls,i+1))))
878    {
879        int64_t pos;
880        const char * newval;
881
882        if(    tr_bencGetInt( pair[0], &pos )
883            && tr_bencGetStr( pair[1], &newval )
884            && tr_urlIsValid( newval, -1 )
885            && pos < n
886            && pos >= 0 )
887        {
888            tr_free( trackers[pos].announce );
889            trackers[pos].announce = tr_strdup( newval );
890            changed = true;
891        }
892
893        i += 2;
894    }
895
896    if( !changed )
897        errmsg = "invalid argument";
898    else if( !tr_torrentSetAnnounceList( tor, trackers, n ) )
899        errmsg = "error setting announce list";
900
901    freeTrackers( trackers, n );
902    return errmsg;
903}
904
905static const char*
906removeTrackers( tr_torrent * tor, tr_benc * ids )
907{
908    int i;
909    int n;
910    int t = 0;
911    int dup = -1;
912    int * tids;
913    tr_benc * val;
914    tr_tracker_info * trackers;
915    bool changed = false;
916    const tr_info * inf = tr_torrentInfo( tor );
917    const char * errmsg = NULL;
918
919    /* make a working copy of the existing announce list */
920    n = inf->trackerCount;
921    tids = tr_new0( int, n );
922    trackers = tr_new0( tr_tracker_info, n );
923    copyTrackers( trackers, inf->trackers, n );
924
925    /* remove the ones specified in the urls list */
926    i = 0;
927    while(( val = tr_bencListChild( ids, i++ )))
928    {
929        int64_t pos;
930
931        if(    tr_bencGetInt( val, &pos )
932            && pos < n
933            && pos >= 0 )
934            tids[t++] = pos;
935    }
936
937    /* sort trackerIds and remove from largest to smallest so there is no need to recacluate array indicies */
938    qsort( tids, t, sizeof(int), compareInt );
939    while( t-- )
940    {
941        /* check for duplicates */
942        if( tids[t] == dup )
943            continue;
944        tr_removeElementFromArray( trackers, tids[t], sizeof( tr_tracker_info ), n-- );
945        dup = tids[t];
946        changed = true;
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    tr_free( tids );
956    return errmsg;
957}
958
959static const char*
960torrentSet( tr_session               * session,
961            tr_benc                  * args_in,
962            tr_benc                  * args_out UNUSED,
963            struct tr_rpc_idle_data  * idle_data UNUSED )
964{
965    const char * errmsg = NULL;
966    int i, torrentCount;
967    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
968
969    assert( idle_data == NULL );
970
971    for( i = 0; i < torrentCount; ++i )
972    {
973        int64_t      tmp;
974        double       d;
975        tr_benc *    files;
976        tr_benc *    trackers;
977        bool         boolVal;
978        tr_torrent * tor = torrents[i];
979
980        if( tr_bencDictFindInt( args_in, "bandwidthPriority", &tmp ) )
981            if( tr_isPriority( tmp ) )
982                tr_torrentSetPriority( tor, tmp );
983        if( !errmsg && tr_bencDictFindList( args_in, "files-unwanted", &files ) )
984            errmsg = setFileDLs( tor, false, files );
985        if( !errmsg && tr_bencDictFindList( args_in, "files-wanted", &files ) )
986            errmsg = setFileDLs( tor, true, files );
987        if( tr_bencDictFindInt( args_in, "peer-limit", &tmp ) )
988            tr_torrentSetPeerLimit( tor, tmp );
989        if( !errmsg &&  tr_bencDictFindList( args_in, "priority-high", &files ) )
990            errmsg = setFilePriorities( tor, TR_PRI_HIGH, files );
991        if( !errmsg && tr_bencDictFindList( args_in, "priority-low", &files ) )
992            errmsg = setFilePriorities( tor, TR_PRI_LOW, files );
993        if( !errmsg && tr_bencDictFindList( args_in, "priority-normal", &files ) )
994            errmsg = setFilePriorities( tor, TR_PRI_NORMAL, files );
995        if( tr_bencDictFindInt( args_in, "downloadLimit", &tmp ) )
996            tr_torrentSetSpeedLimit_KBps( tor, TR_DOWN, tmp );
997        if( tr_bencDictFindBool( args_in, "downloadLimited", &boolVal ) )
998            tr_torrentUseSpeedLimit( tor, TR_DOWN, boolVal );
999        if( tr_bencDictFindBool( args_in, "honorsSessionLimits", &boolVal ) )
1000            tr_torrentUseSessionLimits( tor, boolVal );
1001        if( tr_bencDictFindInt( args_in, "uploadLimit", &tmp ) )
1002            tr_torrentSetSpeedLimit_KBps( tor, TR_UP, tmp );
1003        if( tr_bencDictFindBool( args_in, "uploadLimited", &boolVal ) )
1004            tr_torrentUseSpeedLimit( tor, TR_UP, boolVal );
1005        if( tr_bencDictFindInt( args_in, "seedIdleLimit", &tmp ) )
1006            tr_torrentSetIdleLimit( tor, tmp );
1007        if( tr_bencDictFindInt( args_in, "seedIdleMode", &tmp ) )
1008            tr_torrentSetIdleMode( tor, tmp );
1009        if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
1010            tr_torrentSetRatioLimit( tor, d );
1011        if( tr_bencDictFindInt( args_in, "seedRatioMode", &tmp ) )
1012            tr_torrentSetRatioMode( tor, tmp );
1013        if( !errmsg && tr_bencDictFindList( args_in, "trackerAdd", &trackers ) )
1014            errmsg = addTrackerUrls( tor, trackers );
1015        if( !errmsg && tr_bencDictFindList( args_in, "trackerRemove", &trackers ) )
1016            errmsg = removeTrackers( tor, trackers );
1017        if( !errmsg && tr_bencDictFindList( args_in, "trackerReplace", &trackers ) )
1018            errmsg = replaceTrackers( tor, trackers );
1019        notify( session, TR_RPC_TORRENT_CHANGED, tor );
1020    }
1021
1022    tr_free( torrents );
1023    return errmsg;
1024}
1025
1026static const char*
1027torrentSetLocation( tr_session               * session,
1028                    tr_benc                  * args_in,
1029                    tr_benc                  * args_out UNUSED,
1030                    struct tr_rpc_idle_data  * idle_data UNUSED )
1031{
1032    const char * errmsg = NULL;
1033    const char * location = NULL;
1034
1035    assert( idle_data == NULL );
1036
1037    if( !tr_bencDictFindStr( args_in, "location", &location ) )
1038    {
1039        errmsg = "no location";
1040    }
1041    else
1042    {
1043        bool move = false;
1044        int i, torrentCount;
1045        tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
1046
1047        tr_bencDictFindBool( args_in, "move", &move );
1048
1049        for( i=0; i<torrentCount; ++i )
1050        {
1051            tr_torrent * tor = torrents[i];
1052            tr_torrentSetLocation( tor, location, move, NULL, NULL );
1053            notify( session, TR_RPC_TORRENT_MOVED, tor );
1054        }
1055
1056        tr_free( torrents );
1057    }
1058
1059    return errmsg;
1060}
1061
1062/***
1063****
1064***/
1065
1066static void
1067portTested( tr_session       * session UNUSED,
1068            bool               did_connect UNUSED,
1069            bool               did_timeout UNUSED,
1070            long               response_code,
1071            const void       * response,
1072            size_t             response_byte_count,
1073            void             * user_data )
1074{
1075    char result[1024];
1076    struct tr_rpc_idle_data * data = user_data;
1077
1078    if( response_code != 200 )
1079    {
1080        tr_snprintf( result, sizeof( result ), "portTested: http error %ld: %s",
1081                     response_code, tr_webGetResponseStr( response_code ) );
1082    }
1083    else /* success */
1084    {
1085        const bool isOpen = response_byte_count && *(char*)response == '1';
1086        tr_bencDictAddBool( data->args_out, "port-is-open", isOpen );
1087        tr_snprintf( result, sizeof( result ), "success" );
1088    }
1089
1090    tr_idle_function_done( data, result );
1091}
1092
1093static const char*
1094portTest( tr_session               * session,
1095          tr_benc                  * args_in UNUSED,
1096          tr_benc                  * args_out UNUSED,
1097          struct tr_rpc_idle_data  * idle_data )
1098{
1099    const int port = tr_sessionGetPeerPort( session );
1100    char * url = tr_strdup_printf( "http://portcheck.transmissionbt.com/%d", port );
1101    tr_webRun( session, url, NULL, NULL, portTested, idle_data );
1102    tr_free( url );
1103    return NULL;
1104}
1105
1106/***
1107****
1108***/
1109
1110static void
1111gotNewBlocklist( tr_session       * session,
1112                 bool               did_connect UNUSED,
1113                 bool               did_timeout UNUSED,
1114                 long               response_code,
1115                 const void       * response,
1116                 size_t             response_byte_count,
1117                 void             * user_data )
1118{
1119    char result[1024];
1120    struct tr_rpc_idle_data * data = user_data;
1121
1122    if( response_code != 200 )
1123    {
1124        tr_snprintf( result, sizeof( result ), "gotNewBlocklist: http error %ld: %s",
1125                     response_code, tr_webGetResponseStr( response_code ) );
1126    }
1127    else /* successfully fetched the blocklist... */
1128    {
1129        int fd;
1130        const char * configDir = tr_sessionGetConfigDir( session );
1131        char * filename = tr_buildPath( configDir, "blocklist.tmp", NULL );
1132
1133        errno = 0;
1134
1135        fd = tr_open_file_for_writing( filename );
1136        if( fd < 0 )
1137            tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename, tr_strerror( errno ) );
1138
1139        if( !errno ) {
1140            const char * buf = response;
1141            size_t buflen = response_byte_count;
1142            while( buflen > 0 ) {
1143                int n = write( fd, buf, buflen );
1144                if( n < 0 ) {
1145                    tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename, tr_strerror( errno ) );
1146                    break;
1147                }
1148                buf += n;
1149                buflen -= n;
1150            }
1151            tr_close_file( fd );
1152        }
1153
1154#ifdef HAVE_ZLIB
1155        if( !errno )
1156        {
1157            char * filename2 = tr_buildPath( configDir, "blocklist.txt.tmp", NULL );
1158            fd = tr_open_file_for_writing( filename2 );
1159            if( fd < 0 )
1160                tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename2, tr_strerror( errno ) );
1161            else {
1162                gzFile gzf = gzopen( filename, "r" );
1163                if( gzf ) {
1164                    const size_t buflen = 1024 * 128; /* 128 KiB buffer */
1165                    uint8_t * buf = tr_valloc( buflen );
1166                    for( ;; ) {
1167                        int n = gzread( gzf, buf, buflen );
1168                        if( n < 0 ) /* error */
1169                            tr_snprintf( result, sizeof( result ), _( "Error reading \"%1$s\": %2$s" ), filename, gzerror( gzf, NULL ) );
1170                        if( n < 1 ) /* error or EOF */
1171                            break;
1172                        if( write( fd, buf, n ) < 0 )
1173                            tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename2, tr_strerror( errno ) );
1174                    }
1175                    tr_free( buf );
1176                    gzclose( gzf );
1177                }
1178                tr_close_file( fd );
1179            }
1180
1181            unlink( filename );
1182            tr_free( filename );
1183            filename = filename2;
1184        }
1185#endif
1186
1187        if( !errno ) {
1188            /* feed it to the session and give the client a response */
1189            const int rule_count = tr_blocklistSetContent( session, filename );
1190            tr_bencDictAddInt( data->args_out, "blocklist-size", rule_count );
1191            tr_snprintf( result, sizeof( result ), "success" );
1192        }
1193
1194        unlink( filename );
1195        tr_free( filename );
1196    }
1197
1198    tr_idle_function_done( data, result );
1199}
1200
1201static const char*
1202blocklistUpdate( tr_session               * session,
1203                 tr_benc                  * args_in UNUSED,
1204                 tr_benc                  * args_out UNUSED,
1205                 struct tr_rpc_idle_data  * idle_data )
1206{
1207    tr_webRun( session, session->blocklist_url, NULL, NULL, gotNewBlocklist, idle_data );
1208    return NULL;
1209}
1210
1211/***
1212****
1213***/
1214
1215static void
1216addTorrentImpl( struct tr_rpc_idle_data * data, tr_ctor * ctor )
1217{
1218    int err = 0;
1219    const char * result = NULL;
1220    tr_torrent * tor = tr_torrentNew( ctor, &err );
1221
1222    tr_ctorFree( ctor );
1223
1224    if( tor )
1225    {
1226        tr_benc fields;
1227        tr_bencInitList( &fields, 3 );
1228        tr_bencListAddStr( &fields, "id" );
1229        tr_bencListAddStr( &fields, "name" );
1230        tr_bencListAddStr( &fields, "hashString" );
1231        addInfo( tor, tr_bencDictAdd( data->args_out, "torrent-added" ), &fields );
1232        notify( data->session, TR_RPC_TORRENT_ADDED, tor );
1233        tr_bencFree( &fields );
1234    }
1235    else if( err == TR_PARSE_DUPLICATE )
1236    {
1237        result = "duplicate torrent";
1238    }
1239    else if( err == TR_PARSE_ERR )
1240    {
1241        result = "invalid or corrupt torrent file";
1242    }
1243
1244    tr_idle_function_done( data, result );
1245}
1246
1247
1248struct add_torrent_idle_data
1249{
1250    struct tr_rpc_idle_data * data;
1251    tr_ctor * ctor;
1252};
1253
1254static void
1255gotMetadataFromURL( tr_session       * session UNUSED,
1256                    bool               did_connect UNUSED,
1257                    bool               did_timeout UNUSED,
1258                    long               response_code,
1259                    const void       * response,
1260                    size_t             response_byte_count,
1261                    void             * user_data )
1262{
1263    struct add_torrent_idle_data * data = user_data;
1264
1265    dbgmsg( "torrentAdd: HTTP response code was %ld (%s); response length was %zu bytes",
1266            response_code, tr_webGetResponseStr( response_code ), response_byte_count );
1267
1268    if( response_code==200 || response_code==221 ) /* http or ftp success.. */
1269    {
1270        tr_ctorSetMetainfo( data->ctor, response, response_byte_count );
1271        addTorrentImpl( data->data, data->ctor );
1272    }
1273    else
1274    {
1275        char result[1024];
1276        tr_snprintf( result, sizeof( result ), "gotMetadataFromURL: http error %ld: %s",
1277                     response_code, tr_webGetResponseStr( response_code ) );
1278        tr_idle_function_done( data->data, result );
1279    }
1280
1281    tr_free( data );
1282}
1283
1284static bool
1285isCurlURL( const char * filename )
1286{
1287    if( filename == NULL )
1288        return false;
1289
1290    return !strncmp( filename, "ftp://", 6 ) ||
1291           !strncmp( filename, "http://", 7 ) ||
1292           !strncmp( filename, "https://", 8 );
1293}
1294
1295static tr_file_index_t*
1296fileListFromList( tr_benc * list, tr_file_index_t * setmeCount )
1297{
1298    size_t i;
1299    const size_t childCount = tr_bencListSize( list );
1300    tr_file_index_t n = 0;
1301    tr_file_index_t * files = tr_new0( tr_file_index_t, childCount );
1302
1303    for( i=0; i<childCount; ++i ) {
1304        int64_t intVal;
1305        if( tr_bencGetInt( tr_bencListChild( list, i ), &intVal ) )
1306            files[n++] = (tr_file_index_t)intVal;
1307    }
1308
1309    *setmeCount = n;
1310    return files;
1311}
1312
1313static const char*
1314torrentAdd( tr_session               * session,
1315            tr_benc                  * args_in,
1316            tr_benc                  * args_out UNUSED,
1317            struct tr_rpc_idle_data  * idle_data )
1318{
1319    const char * filename = NULL;
1320    const char * metainfo_base64 = NULL;
1321
1322    assert( idle_data != NULL );
1323
1324    tr_bencDictFindStr( args_in, "filename", &filename );
1325    tr_bencDictFindStr( args_in, "metainfo", &metainfo_base64 );
1326    if( !filename && !metainfo_base64 )
1327        return "no filename or metainfo specified";
1328    else
1329    {
1330        int64_t      i;
1331        bool         boolVal;
1332        tr_benc    * l;
1333        const char * str;
1334        const char * cookies = NULL;
1335        tr_ctor    * ctor = tr_ctorNew( session );
1336
1337        /* set the optional arguments */
1338
1339        tr_bencDictFindStr( args_in, "cookies", &cookies );
1340
1341        if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1342            tr_ctorSetDownloadDir( ctor, TR_FORCE, str );
1343
1344        if( tr_bencDictFindBool( args_in, "paused", &boolVal ) )
1345            tr_ctorSetPaused( ctor, TR_FORCE, boolVal );
1346
1347        if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
1348            tr_ctorSetPeerLimit( ctor, TR_FORCE, i );
1349
1350        if( tr_bencDictFindInt( args_in, "bandwidthPriority", &i ) )
1351            tr_ctorSetBandwidthPriority( ctor, i );
1352
1353        if( tr_bencDictFindList( args_in, "files-unwanted", &l ) ) {
1354            tr_file_index_t fileCount;
1355            tr_file_index_t * files = fileListFromList( l, &fileCount );
1356            tr_ctorSetFilesWanted( ctor, files, fileCount, false );
1357            tr_free( files );
1358        }
1359        if( tr_bencDictFindList( args_in, "files-wanted", &l ) ) {
1360            tr_file_index_t fileCount;
1361            tr_file_index_t * files = fileListFromList( l, &fileCount );
1362            tr_ctorSetFilesWanted( ctor, files, fileCount, true );
1363            tr_free( files );
1364        }
1365
1366        if( tr_bencDictFindList( args_in, "priority-low", &l ) ) {
1367            tr_file_index_t fileCount;
1368            tr_file_index_t * files = fileListFromList( l, &fileCount );
1369            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_LOW );
1370            tr_free( files );
1371        }
1372        if( tr_bencDictFindList( args_in, "priority-normal", &l ) ) {
1373            tr_file_index_t fileCount;
1374            tr_file_index_t * files = fileListFromList( l, &fileCount );
1375            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_NORMAL );
1376            tr_free( files );
1377        }
1378        if( tr_bencDictFindList( args_in, "priority-high", &l ) ) {
1379            tr_file_index_t fileCount;
1380            tr_file_index_t * files = fileListFromList( l, &fileCount );
1381            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_HIGH );
1382            tr_free( files );
1383        }
1384
1385        dbgmsg( "torrentAdd: filename is \"%s\"", filename ? filename : "(null)" );
1386
1387        if( isCurlURL( filename ) )
1388        {
1389            struct add_torrent_idle_data * d = tr_new0( struct add_torrent_idle_data, 1 );
1390            d->data = idle_data;
1391            d->ctor = ctor;
1392            tr_webRun( session, filename, NULL, cookies, gotMetadataFromURL, d );
1393        }
1394        else
1395        {
1396            char * fname = tr_strstrip( tr_strdup( filename ) );
1397
1398            if( fname == NULL )
1399            {
1400                int len;
1401                char * metainfo = tr_base64_decode( metainfo_base64, -1, &len );
1402                tr_ctorSetMetainfo( ctor, (uint8_t*)metainfo, len );
1403                tr_free( metainfo );
1404            }
1405            else if( !strncmp( fname, "magnet:?", 8 ) )
1406            {
1407                tr_ctorSetMetainfoFromMagnetLink( ctor, fname );
1408            }
1409            else
1410            {
1411                tr_ctorSetMetainfoFromFile( ctor, fname );
1412            }
1413
1414            addTorrentImpl( idle_data, ctor );
1415
1416            tr_free( fname );
1417        }
1418
1419    }
1420
1421    return NULL;
1422}
1423
1424/***
1425****
1426***/
1427
1428static const char*
1429sessionSet( tr_session               * session,
1430            tr_benc                  * args_in,
1431            tr_benc                  * args_out UNUSED,
1432            struct tr_rpc_idle_data  * idle_data UNUSED )
1433{
1434    int64_t      i;
1435    double       d;
1436    bool         boolVal;
1437    const char * str;
1438
1439    assert( idle_data == NULL );
1440
1441    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, &i ) )
1442        tr_sessionSetCacheLimit_MB( session, i );
1443    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_UP_KBps, &i ) )
1444        tr_sessionSetAltSpeed_KBps( session, TR_UP, i );
1445    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, &i ) )
1446        tr_sessionSetAltSpeed_KBps( session, TR_DOWN, i );
1447    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_ENABLED, &boolVal ) )
1448        tr_sessionUseAltSpeed( session, boolVal );
1449    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &i ) )
1450        tr_sessionSetAltSpeedBegin( session, i );
1451    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_END, &i ) )
1452        tr_sessionSetAltSpeedEnd( session, i );
1453    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &i ) )
1454        tr_sessionSetAltSpeedDay( session, i );
1455    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &boolVal ) )
1456        tr_sessionUseAltSpeedTime( session, boolVal );
1457    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_BLOCKLIST_ENABLED, &boolVal ) )
1458        tr_blocklistSetEnabled( session, boolVal );
1459    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_BLOCKLIST_URL, &str ) )
1460        tr_blocklistSetURL( session, str );
1461    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1462        tr_sessionSetDownloadDir( session, str );
1463    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_INCOMPLETE_DIR, &str ) )
1464        tr_sessionSetIncompleteDir( session, str );
1465    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, &boolVal ) )
1466        tr_sessionSetIncompleteDirEnabled( session, boolVal );
1467    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &i ) )
1468        tr_sessionSetPeerLimit( session, i );
1469    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i ) )
1470        tr_sessionSetPeerLimitPerTorrent( session, i );
1471    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) )
1472        tr_sessionSetPexEnabled( session, boolVal );
1473    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DHT_ENABLED, &boolVal ) )
1474        tr_sessionSetDHTEnabled( session, boolVal );
1475    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_UTP_ENABLED, &boolVal ) )
1476        tr_sessionSetUTPEnabled( session, boolVal );
1477    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_LPD_ENABLED, &boolVal ) )
1478        tr_sessionSetLPDEnabled( session, boolVal );
1479    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, &boolVal ) )
1480        tr_sessionSetPeerPortRandomOnStart( session, boolVal );
1481    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_PORT, &i ) )
1482        tr_sessionSetPeerPort( session, i );
1483    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) )
1484        tr_sessionSetPortForwardingEnabled( session, boolVal );
1485    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_RENAME_PARTIAL_FILES, &boolVal ) )
1486        tr_sessionSetIncompleteFileNamingEnabled( session, boolVal );
1487    if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
1488        tr_sessionSetRatioLimit( session, d );
1489    if( tr_bencDictFindBool( args_in, "seedRatioLimited", &boolVal ) )
1490        tr_sessionSetRatioLimited( session, boolVal );
1491    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_IDLE_LIMIT, &i ) )
1492        tr_sessionSetIdleLimit( session, i );
1493    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, &boolVal ) )
1494        tr_sessionSetIdleLimited( session, boolVal );
1495    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_START, &boolVal ) )
1496        tr_sessionSetPaused( session, !boolVal );
1497    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, &str ) )
1498        tr_sessionSetTorrentDoneScript( session, str );
1499    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, &boolVal ) )
1500        tr_sessionSetTorrentDoneScriptEnabled( session, boolVal );
1501    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_TRASH_ORIGINAL, &boolVal ) )
1502        tr_sessionSetDeleteSource( session, boolVal );
1503    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_DSPEED_KBps, &i ) )
1504        tr_sessionSetSpeedLimit_KBps( session, TR_DOWN, i );
1505    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DSPEED_ENABLED, &boolVal ) )
1506        tr_sessionLimitSpeed( session, TR_DOWN, boolVal );
1507    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_USPEED_KBps, &i ) )
1508        tr_sessionSetSpeedLimit_KBps( session, TR_UP, i );
1509    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_USPEED_ENABLED, &boolVal ) )
1510        tr_sessionLimitSpeed( session, TR_UP, boolVal );
1511    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_ENCRYPTION, &str ) ) {
1512        if( !strcmp( str, "required" ) )
1513            tr_sessionSetEncryption( session, TR_ENCRYPTION_REQUIRED );
1514        else if( !strcmp( str, "tolerated" ) )
1515            tr_sessionSetEncryption( session, TR_CLEAR_PREFERRED );
1516        else
1517            tr_sessionSetEncryption( session, TR_ENCRYPTION_PREFERRED );
1518    }
1519
1520    notify( session, TR_RPC_SESSION_CHANGED, NULL );
1521
1522    return NULL;
1523}
1524
1525static const char*
1526sessionStats( tr_session               * session,
1527              tr_benc                  * args_in UNUSED,
1528              tr_benc                  * args_out,
1529              struct tr_rpc_idle_data  * idle_data UNUSED )
1530{
1531    int running = 0;
1532    int total = 0;
1533    tr_benc * d;
1534    tr_session_stats currentStats = { 0.0f, 0, 0, 0, 0, 0 };
1535    tr_session_stats cumulativeStats = { 0.0f, 0, 0, 0, 0, 0 };
1536    tr_torrent * tor = NULL;
1537
1538    assert( idle_data == NULL );
1539
1540    while(( tor = tr_torrentNext( session, tor ))) {
1541        ++total;
1542        if( tor->isRunning )
1543            ++running;
1544    }
1545
1546    tr_sessionGetStats( session, &currentStats );
1547    tr_sessionGetCumulativeStats( session, &cumulativeStats );
1548
1549    tr_bencDictAddInt ( args_out, "activeTorrentCount", running );
1550    tr_bencDictAddReal( args_out, "downloadSpeed", tr_sessionGetPieceSpeed_Bps( session, TR_DOWN ) );
1551    tr_bencDictAddInt ( args_out, "pausedTorrentCount", total - running );
1552    tr_bencDictAddInt ( args_out, "torrentCount", total );
1553    tr_bencDictAddReal( args_out, "uploadSpeed", tr_sessionGetPieceSpeed_Bps( session, TR_UP ) );
1554
1555    d = tr_bencDictAddDict( args_out, "cumulative-stats", 5 );
1556    tr_bencDictAddInt( d, "downloadedBytes", cumulativeStats.downloadedBytes );
1557    tr_bencDictAddInt( d, "filesAdded", cumulativeStats.filesAdded );
1558    tr_bencDictAddInt( d, "secondsActive", cumulativeStats.secondsActive );
1559    tr_bencDictAddInt( d, "sessionCount", cumulativeStats.sessionCount );
1560    tr_bencDictAddInt( d, "uploadedBytes", cumulativeStats.uploadedBytes );
1561
1562    d = tr_bencDictAddDict( args_out, "current-stats", 5 );
1563    tr_bencDictAddInt( d, "downloadedBytes", currentStats.downloadedBytes );
1564    tr_bencDictAddInt( d, "filesAdded", currentStats.filesAdded );
1565    tr_bencDictAddInt( d, "secondsActive", currentStats.secondsActive );
1566    tr_bencDictAddInt( d, "sessionCount", currentStats.sessionCount );
1567    tr_bencDictAddInt( d, "uploadedBytes", currentStats.uploadedBytes );
1568
1569    return NULL;
1570}
1571
1572static const char*
1573sessionGet( tr_session               * s,
1574            tr_benc                  * args_in UNUSED,
1575            tr_benc                  * args_out,
1576            struct tr_rpc_idle_data  * idle_data UNUSED )
1577{
1578    const char * str;
1579    tr_benc * d = args_out;
1580
1581    assert( idle_data == NULL );
1582    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP_KBps, tr_sessionGetAltSpeed_KBps(s,TR_UP) );
1583    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, tr_sessionGetAltSpeed_KBps(s,TR_DOWN) );
1584    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED, tr_sessionUsesAltSpeed(s) );
1585    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, tr_sessionGetAltSpeedBegin(s) );
1586    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,tr_sessionGetAltSpeedEnd(s) );
1587    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,tr_sessionGetAltSpeedDay(s) );
1588    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, tr_sessionUsesAltSpeedTime(s) );
1589    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, tr_blocklistIsEnabled( s ) );
1590    tr_bencDictAddStr ( d, TR_PREFS_KEY_BLOCKLIST_URL, tr_blocklistGetURL( s ) );
1591    tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, tr_sessionGetCacheLimit_MB( s ) );
1592    tr_bencDictAddInt ( d, "blocklist-size", tr_blocklistGetRuleCount( s ) );
1593    tr_bencDictAddStr ( d, "config-dir", tr_sessionGetConfigDir( s ) );
1594    tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR, tr_sessionGetDownloadDir( s ) );
1595    tr_bencDictAddInt ( d, "download-dir-free-space",  tr_sessionGetDownloadDirFreeSpace( s ) );
1596    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, tr_sessionGetPeerLimit( s ) );
1597    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT, tr_sessionGetPeerLimitPerTorrent( s ) );
1598    tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR, tr_sessionGetIncompleteDir( s ) );
1599    tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, tr_sessionIsIncompleteDirEnabled( s ) );
1600    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED, tr_sessionIsPexEnabled( s ) );
1601    tr_bencDictAddBool( d, TR_PREFS_KEY_UTP_ENABLED, tr_sessionIsUTPEnabled( s ) );
1602    tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED, tr_sessionIsDHTEnabled( s ) );
1603    tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED, tr_sessionIsLPDEnabled( s ) );
1604    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT, tr_sessionGetPeerPort( s ) );
1605    tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, tr_sessionGetPeerPortRandomOnStart( s ) );
1606    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) );
1607    tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES, tr_sessionIsIncompleteFileNamingEnabled( s ) );
1608    tr_bencDictAddInt ( d, "rpc-version", RPC_VERSION );
1609    tr_bencDictAddInt ( d, "rpc-version-minimum", RPC_VERSION_MIN );
1610    tr_bencDictAddReal( d, "seedRatioLimit", tr_sessionGetRatioLimit( s ) );
1611    tr_bencDictAddBool( d, "seedRatioLimited", tr_sessionIsRatioLimited( s ) );
1612    tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT, tr_sessionGetIdleLimit( s ) );
1613    tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, tr_sessionIsIdleLimited( s ) );
1614    tr_bencDictAddBool( d, TR_PREFS_KEY_START, !tr_sessionGetPaused( s ) );
1615    tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL, tr_sessionGetDeleteSource( s ) );
1616    tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_UP ) );
1617    tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_UP ) );
1618    tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_DOWN ) );
1619    tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_DOWN ) );
1620    tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, tr_sessionGetTorrentDoneScript( s ) );
1621    tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, tr_sessionIsTorrentDoneScriptEnabled( s ) );
1622    tr_formatter_get_units( tr_bencDictAddDict( d, "units", 0 ) );
1623    tr_bencDictAddStr ( d, "version", LONG_VERSION_STRING );
1624    switch( tr_sessionGetEncryption( s ) ) {
1625        case TR_CLEAR_PREFERRED: str = "tolerated"; break;
1626        case TR_ENCRYPTION_REQUIRED: str = "required"; break;
1627        default: str = "preferred"; break;
1628    }
1629    tr_bencDictAddStr( d, TR_PREFS_KEY_ENCRYPTION, str );
1630
1631    return NULL;
1632}
1633
1634/***
1635****
1636***/
1637
1638static const char*
1639sessionClose( tr_session               * session,
1640              tr_benc                  * args_in UNUSED,
1641              tr_benc                  * args_out UNUSED,
1642              struct tr_rpc_idle_data  * idle_data UNUSED )
1643{
1644    notify( session, TR_RPC_SESSION_CLOSE, NULL );
1645    return NULL;
1646}
1647
1648/***
1649****
1650***/
1651
1652typedef const char* ( *handler )( tr_session*, tr_benc*, tr_benc*, struct tr_rpc_idle_data * );
1653
1654static struct method
1655{
1656    const char *  name;
1657    bool          immediate;
1658    handler       func;
1659}
1660methods[] =
1661{
1662    { "port-test",             false, portTest            },
1663    { "blocklist-update",      false, blocklistUpdate     },
1664    { "session-close",         true,  sessionClose        },
1665    { "session-get",           true,  sessionGet          },
1666    { "session-set",           true,  sessionSet          },
1667    { "session-stats",         true,  sessionStats        },
1668    { "torrent-add",           false, torrentAdd          },
1669    { "torrent-get",           true,  torrentGet          },
1670    { "torrent-remove",        true,  torrentRemove       },
1671    { "torrent-set",           true,  torrentSet          },
1672    { "torrent-set-location",  true,  torrentSetLocation  },
1673    { "torrent-start",         true,  torrentStart        },
1674    { "torrent-stop",          true,  torrentStop         },
1675    { "torrent-verify",        true,  torrentVerify       },
1676    { "torrent-reannounce",    true,  torrentReannounce   }
1677};
1678
1679static void
1680noop_response_callback( tr_session       * session UNUSED,
1681                        struct evbuffer  * response UNUSED,
1682                        void             * user_data UNUSED )
1683{
1684}
1685
1686static void
1687request_exec( tr_session             * session,
1688              tr_benc                * request,
1689              tr_rpc_response_func     callback,
1690              void                   * callback_user_data )
1691{
1692    int i;
1693    const char * str;
1694    tr_benc * args_in = tr_bencDictFind( request, "arguments" );
1695    const char * result = NULL;
1696
1697    if( callback == NULL )
1698        callback = noop_response_callback;
1699
1700    /* parse the request */
1701    if( !tr_bencDictFindStr( request, "method", &str ) )
1702        result = "no method name";
1703    else {
1704        const int n = TR_N_ELEMENTS( methods );
1705        for( i = 0; i < n; ++i )
1706            if( !strcmp( str, methods[i].name ) )
1707                break;
1708        if( i ==n )
1709            result = "method name not recognized";
1710    }
1711
1712    /* if we couldn't figure out which method to use, return an error */
1713    if( result != NULL )
1714    {
1715        int64_t tag;
1716        tr_benc response;
1717        struct evbuffer * buf = evbuffer_new( );
1718
1719        tr_bencInitDict( &response, 3 );
1720        tr_bencDictAddDict( &response, "arguments", 0 );
1721        tr_bencDictAddStr( &response, "result", result );
1722        if( tr_bencDictFindInt( request, "tag", &tag ) )
1723            tr_bencDictAddInt( &response, "tag", tag );
1724        tr_bencToBuf( &response, TR_FMT_JSON_LEAN, buf );
1725        (*callback)( session, buf, callback_user_data );
1726
1727        evbuffer_free( buf );
1728        tr_bencFree( &response );
1729    }
1730    else if( methods[i].immediate )
1731    {
1732        int64_t tag;
1733        tr_benc response;
1734        tr_benc * args_out;
1735        struct evbuffer * buf = evbuffer_new( );
1736
1737        tr_bencInitDict( &response, 3 );
1738        args_out = tr_bencDictAddDict( &response, "arguments", 0 );
1739        result = (*methods[i].func)( session, args_in, args_out, NULL );
1740        if( result == NULL )
1741            result = "success";
1742        tr_bencDictAddStr( &response, "result", result );
1743        if( tr_bencDictFindInt( request, "tag", &tag ) )
1744            tr_bencDictAddInt( &response, "tag", tag );
1745        tr_bencToBuf( &response, TR_FMT_JSON_LEAN, buf );
1746        (*callback)( session, buf, callback_user_data );
1747
1748        evbuffer_free( buf );
1749        tr_bencFree( &response );
1750    }
1751    else
1752    {
1753        int64_t tag;
1754        struct tr_rpc_idle_data * data = tr_new0( struct tr_rpc_idle_data, 1 );
1755        data->session = session;
1756        data->response = tr_new0( tr_benc, 1 );
1757        tr_bencInitDict( data->response, 3 );
1758        if( tr_bencDictFindInt( request, "tag", &tag ) )
1759            tr_bencDictAddInt( data->response, "tag", tag );
1760        data->args_out = tr_bencDictAddDict( data->response, "arguments", 0 );
1761        data->callback = callback;
1762        data->callback_user_data = callback_user_data;
1763        (*methods[i].func)( session, args_in, data->args_out, data );
1764    }
1765}
1766
1767void
1768tr_rpc_request_exec_json( tr_session            * session,
1769                          const void            * request_json,
1770                          int                     request_len,
1771                          tr_rpc_response_func    callback,
1772                          void                  * callback_user_data )
1773{
1774    tr_benc top;
1775    int have_content;
1776
1777    if( request_len < 0 )
1778        request_len = strlen( request_json );
1779
1780    have_content = !tr_jsonParse( "rpc", request_json, request_len, &top, NULL );
1781    request_exec( session, have_content ? &top : NULL, callback, callback_user_data );
1782
1783    if( have_content )
1784        tr_bencFree( &top );
1785}
1786
1787/**
1788 * Munge the URI into a usable form.
1789 *
1790 * We have very loose typing on this to make the URIs as simple as possible:
1791 * - anything not a 'tag' or 'method' is automatically in 'arguments'
1792 * - values that are all-digits are numbers
1793 * - values that are all-digits or commas are number lists
1794 * - all other values are strings
1795 */
1796void
1797tr_rpc_parse_list_str( tr_benc     * setme,
1798                       const char  * str,
1799                       int           len )
1800
1801{
1802    int valueCount;
1803    int * values = tr_parseNumberRange( str, len, &valueCount );
1804
1805    if( valueCount == 0 )
1806        tr_bencInitStr( setme, str, len );
1807    else if( valueCount == 1 )
1808        tr_bencInitInt( setme, values[0] );
1809    else {
1810        int i;
1811        tr_bencInitList( setme, valueCount );
1812        for( i=0; i<valueCount; ++i )
1813            tr_bencListAddInt( setme, values[i] );
1814    }
1815
1816    tr_free( values );
1817}
1818
1819void
1820tr_rpc_request_exec_uri( tr_session           * session,
1821                         const void           * request_uri,
1822                         int                    request_len,
1823                         tr_rpc_response_func   callback,
1824                         void                 * callback_user_data )
1825{
1826    tr_benc      top, * args;
1827    char *       request = tr_strndup( request_uri, request_len );
1828    const char * pch;
1829
1830    tr_bencInitDict( &top, 3 );
1831    args = tr_bencDictAddDict( &top, "arguments", 0 );
1832
1833    pch = strchr( request, '?' );
1834    if( !pch ) pch = request;
1835    while( pch )
1836    {
1837        const char * delim = strchr( pch, '=' );
1838        const char * next = strchr( pch, '&' );
1839        if( delim )
1840        {
1841            char *    key = tr_strndup( pch, delim - pch );
1842            int       isArg = strcmp( key, "method" ) && strcmp( key, "tag" );
1843            tr_benc * parent = isArg ? args : &top;
1844            tr_rpc_parse_list_str( tr_bencDictAdd( parent, key ),
1845                                  delim + 1,
1846                                  next ? (size_t)(
1847                                       next -
1848                                      ( delim + 1 ) ) : strlen( delim + 1 ) );
1849            tr_free( key );
1850        }
1851        pch = next ? next + 1 : NULL;
1852    }
1853
1854    request_exec( session, &top, callback, callback_user_data );
1855
1856    /* cleanup */
1857    tr_bencFree( &top );
1858    tr_free( request );
1859}
Note: See TracBrowser for help on using the repository browser.