source: trunk/libtransmission/rpcimpl.c @ 12143

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

(trunk libT) tweaks by werehamster

  • 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 12143 2011-03-13 05:45:34Z 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 "stats.h"
34#include "torrent.h"
35#include "utils.h"
36#include "version.h"
37#include "web.h"
38
39#define RPC_VERSION     13
40#define RPC_VERSION_MIN 1
41
42#define RECENTLY_ACTIVE_SECONDS 60
43
44#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) )
45
46#if 0
47#define dbgmsg(fmt, ...) \
48    do { \
49        fprintf( stderr, "%s:%d"#fmt, __FILE__, __LINE__, __VA_ARGS__ ); \
50        fprintf( stderr, "\n" ); \
51    } while( 0 )
52#else
53#define dbgmsg( ... ) \
54    do { \
55        if( tr_deepLoggingIsActive( ) ) \
56            tr_deepLog( __FILE__, __LINE__, "RPC", __VA_ARGS__ ); \
57    } while( 0 )
58#endif
59
60
61/***
62****
63***/
64
65static tr_rpc_callback_status
66notify( tr_session * session,
67        int          type,
68        tr_torrent * tor )
69{
70    tr_rpc_callback_status status = 0;
71
72    if( session->rpc_func )
73        status = session->rpc_func( session, type, tor,
74                                    session->rpc_func_user_data );
75
76    return status;
77}
78
79/***
80****
81***/
82
83/* For functions that can't be immediately executed, like torrentAdd,
84 * this is the callback data used to pass a response to the caller
85 * when the task is complete */
86struct tr_rpc_idle_data
87{
88    tr_session            * session;
89    tr_benc               * response;
90    tr_benc               * args_out;
91    tr_rpc_response_func    callback;
92    void                  * callback_user_data;
93};
94
95static void
96tr_idle_function_done( struct tr_rpc_idle_data * data, const char * result )
97{
98    struct evbuffer * buf = evbuffer_new( );
99
100    if( result == NULL )
101        result = "success";
102    tr_bencDictAddStr( data->response, "result", result );
103
104    tr_bencToBuf( data->response, TR_FMT_JSON_LEAN, buf );
105    (*data->callback)( data->session, buf, data->callback_user_data );
106
107    evbuffer_free( buf );
108    tr_bencFree( data->response );
109    tr_free( data->response );
110    tr_free( data );
111}
112
113/***
114****
115***/
116
117static tr_torrent **
118getTorrents( tr_session * session,
119             tr_benc    * args,
120             int        * setmeCount )
121{
122    int           torrentCount = 0;
123    int64_t       id;
124    tr_torrent ** torrents = NULL;
125    tr_benc *     ids;
126    const char * str;
127
128    if( tr_bencDictFindList( args, "ids", &ids ) )
129    {
130        int       i;
131        const int n = tr_bencListSize( ids );
132
133        torrents = tr_new0( tr_torrent *, n );
134
135        for( i = 0; i < n; ++i )
136        {
137            tr_torrent * tor = NULL;
138            tr_benc *    node = tr_bencListChild( ids, i );
139            const char * str;
140            if( tr_bencGetInt( node, &id ) )
141                tor = tr_torrentFindFromId( session, id );
142            else if( tr_bencGetStr( node, &str ) )
143                tor = tr_torrentFindFromHashString( session, str );
144            if( tor )
145                torrents[torrentCount++] = tor;
146        }
147    }
148    else if( tr_bencDictFindInt( args, "ids", &id )
149           || tr_bencDictFindInt( args, "id", &id ) )
150    {
151        tr_torrent * tor;
152        torrents = tr_new0( tr_torrent *, 1 );
153        if( ( tor = tr_torrentFindFromId( session, id ) ) )
154            torrents[torrentCount++] = tor;
155    }
156    else if( tr_bencDictFindStr( args, "ids", &str ) )
157    {
158        if( !strcmp( str, "recently-active" ) )
159        {
160            tr_torrent * tor = NULL;
161            const time_t now = tr_time( );
162            const time_t window = RECENTLY_ACTIVE_SECONDS;
163            const int n = tr_sessionCountTorrents( session );
164            torrents = tr_new0( tr_torrent *, n );
165            while( ( tor = tr_torrentNext( session, tor ) ) )
166                if( tor->anyDate >= now - window )
167                    torrents[torrentCount++] = tor;
168        }
169        else
170        {
171            tr_torrent * tor;
172            torrents = tr_new0( tr_torrent *, 1 );
173            if(( tor = tr_torrentFindFromHashString( session, str )))
174                torrents[torrentCount++] = tor;
175        }
176    }
177    else /* all of them */
178    {
179        tr_torrent * tor = NULL;
180        const int n = tr_sessionCountTorrents( session );
181        torrents = tr_new0( tr_torrent *, n );
182        while( ( tor = tr_torrentNext( session, tor ) ) )
183            torrents[torrentCount++] = tor;
184    }
185
186    *setmeCount = torrentCount;
187    return torrents;
188}
189
190static const char*
191torrentStart( tr_session               * session,
192              tr_benc                  * args_in,
193              tr_benc                  * args_out UNUSED,
194              struct tr_rpc_idle_data  * idle_data UNUSED )
195{
196    int           i, torrentCount;
197    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
198
199    assert( idle_data == NULL );
200
201    for( i = 0; i < torrentCount; ++i )
202    {
203        tr_torrent * tor = torrents[i];
204        if( !tor->isRunning )
205        {
206            tr_torrentStart( tor );
207            notify( session, TR_RPC_TORRENT_STARTED, tor );
208        }
209    }
210    tr_free( torrents );
211    return NULL;
212}
213
214static const char*
215torrentStop( tr_session               * session,
216             tr_benc                  * args_in,
217             tr_benc                  * args_out UNUSED,
218             struct tr_rpc_idle_data  * idle_data UNUSED )
219{
220    int           i, torrentCount;
221    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
222
223    assert( idle_data == NULL );
224
225    for( i = 0; i < torrentCount; ++i )
226    {
227        tr_torrent * tor = torrents[i];
228
229        if( tor->isRunning )
230        {
231            tor->isStopping = TRUE;
232            notify( session, TR_RPC_TORRENT_STOPPED, tor );
233        }
234    }
235    tr_free( torrents );
236    return NULL;
237}
238
239static const char*
240torrentRemove( tr_session               * session,
241               tr_benc                  * args_in,
242               tr_benc                  * args_out UNUSED,
243               struct tr_rpc_idle_data  * idle_data UNUSED )
244{
245    int i;
246    int torrentCount;
247    tr_rpc_callback_type type;
248    tr_bool deleteFlag = FALSE;
249    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
250
251    assert( idle_data == NULL );
252
253    tr_bencDictFindBool( args_in, "delete-local-data", &deleteFlag );
254    type = deleteFlag ? TR_RPC_TORRENT_TRASHING
255                      : TR_RPC_TORRENT_REMOVING;
256
257    for( i=0; i<torrentCount; ++i )
258    {
259        tr_torrent * tor = torrents[i];
260        const tr_rpc_callback_status status = notify( session, type, tor );
261        if( !( status & TR_RPC_NOREMOVE ) )
262            tr_torrentRemove( tor, deleteFlag, NULL );
263    }
264
265    tr_free( torrents );
266    return NULL;
267}
268
269static const char*
270torrentReannounce( tr_session               * session,
271                   tr_benc                  * args_in,
272                   tr_benc                  * args_out UNUSED,
273                   struct tr_rpc_idle_data  * idle_data UNUSED )
274{
275    int i, torrentCount;
276    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
277
278    assert( idle_data == NULL );
279
280    for( i=0; i<torrentCount; ++i )
281    {
282        tr_torrent * tor = torrents[i];
283        if( tr_torrentCanManualUpdate( tor ) )
284        {
285            tr_torrentManualUpdate( tor );
286            notify( session, TR_RPC_TORRENT_CHANGED, tor );
287        }
288    }
289
290    tr_free( torrents );
291    return NULL;
292}
293
294static const char*
295torrentVerify( tr_session               * session,
296               tr_benc                  * args_in,
297               tr_benc                  * args_out UNUSED,
298               struct tr_rpc_idle_data  * idle_data UNUSED )
299{
300    int           i, torrentCount;
301    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
302
303    assert( idle_data == NULL );
304
305    for( i = 0; i < torrentCount; ++i )
306    {
307        tr_torrent * tor = torrents[i];
308        tr_torrentVerify( tor );
309        notify( session, TR_RPC_TORRENT_CHANGED, tor );
310    }
311
312    tr_free( torrents );
313    return NULL;
314}
315
316/***
317****
318***/
319
320static void
321addFileStats( const tr_torrent * tor, tr_benc * list )
322{
323    tr_file_index_t i;
324    tr_file_index_t n;
325    const tr_info * info = tr_torrentInfo( tor );
326    tr_file_stat * files = tr_torrentFiles( tor, &n );
327
328    for( i = 0; i < info->fileCount; ++i )
329    {
330        const tr_file * file = &info->files[i];
331        tr_benc * d = tr_bencListAddDict( list, 3 );
332        tr_bencDictAddInt( d, "bytesCompleted", files[i].bytesCompleted );
333        tr_bencDictAddInt( d, "priority", file->priority );
334        tr_bencDictAddBool( d, "wanted", !file->dnd );
335    }
336
337    tr_torrentFilesFree( files, n );
338}
339
340static void
341addFiles( const tr_torrent * tor,
342          tr_benc *          list )
343{
344    tr_file_index_t i;
345    tr_file_index_t n;
346    const tr_info * info = tr_torrentInfo( tor );
347    tr_file_stat *  files = tr_torrentFiles( tor, &n );
348
349    for( i = 0; i < info->fileCount; ++i )
350    {
351        const tr_file * file = &info->files[i];
352        tr_benc *       d = tr_bencListAddDict( list, 3 );
353        tr_bencDictAddInt( d, "bytesCompleted", files[i].bytesCompleted );
354        tr_bencDictAddInt( d, "length", file->length );
355        tr_bencDictAddStr( d, "name", file->name );
356    }
357
358    tr_torrentFilesFree( files, n );
359}
360
361static void
362addWebseeds( const tr_info * info,
363             tr_benc *       webseeds )
364{
365    int i;
366
367    for( i = 0; i < info->webseedCount; ++i )
368        tr_bencListAddStr( webseeds, info->webseeds[i] );
369}
370
371static void
372addTrackers( const tr_info * info,
373             tr_benc *       trackers )
374{
375    int i;
376
377    for( i = 0; i < info->trackerCount; ++i )
378    {
379        const tr_tracker_info * t = &info->trackers[i];
380        tr_benc *               d = tr_bencListAddDict( trackers, 4 );
381        tr_bencDictAddStr( d, "announce", t->announce );
382        tr_bencDictAddInt( d, "id", t->id );
383        tr_bencDictAddStr( d, "scrape", t->scrape );
384        tr_bencDictAddInt( d, "tier", t->tier );
385    }
386}
387
388static void
389addTrackerStats( const tr_tracker_stat * st, int n, tr_benc * list )
390{
391    int i;
392
393    for( i=0; i<n; ++i )
394    {
395        const tr_tracker_stat * s = &st[i];
396        tr_benc * d = tr_bencListAddDict( list, 26 );
397        tr_bencDictAddStr ( d, "announce", s->announce );
398        tr_bencDictAddInt ( d, "announceState", s->announceState );
399        tr_bencDictAddInt ( d, "downloadCount", s->downloadCount );
400        tr_bencDictAddBool( d, "hasAnnounced", s->hasAnnounced );
401        tr_bencDictAddBool( d, "hasScraped", s->hasScraped );
402        tr_bencDictAddStr ( d, "host", s->host );
403        tr_bencDictAddInt ( d, "id", s->id );
404        tr_bencDictAddBool( d, "isBackup", s->isBackup );
405        tr_bencDictAddInt ( d, "lastAnnouncePeerCount", s->lastAnnouncePeerCount );
406        tr_bencDictAddStr ( d, "lastAnnounceResult", s->lastAnnounceResult );
407        tr_bencDictAddInt ( d, "lastAnnounceStartTime", s->lastAnnounceStartTime );
408        tr_bencDictAddBool( d, "lastAnnounceSucceeded", s->lastAnnounceSucceeded );
409        tr_bencDictAddInt ( d, "lastAnnounceTime", s->lastAnnounceTime );
410        tr_bencDictAddBool( d, "lastAnnounceTimedOut", s->lastAnnounceTimedOut );
411        tr_bencDictAddStr ( d, "lastScrapeResult", s->lastScrapeResult );
412        tr_bencDictAddInt ( d, "lastScrapeStartTime", s->lastScrapeStartTime );
413        tr_bencDictAddBool( d, "lastScrapeSucceeded", s->lastScrapeSucceeded );
414        tr_bencDictAddInt ( d, "lastScrapeTime", s->lastScrapeTime );
415        tr_bencDictAddInt ( d, "lastScrapeTimedOut", s->lastScrapeTimedOut );
416        tr_bencDictAddInt ( d, "leecherCount", s->leecherCount );
417        tr_bencDictAddInt ( d, "nextAnnounceTime", s->nextAnnounceTime );
418        tr_bencDictAddInt ( d, "nextScrapeTime", s->nextScrapeTime );
419        tr_bencDictAddStr ( d, "scrape", s->scrape );
420        tr_bencDictAddInt ( d, "scrapeState", s->scrapeState );
421        tr_bencDictAddInt ( d, "seederCount", s->seederCount );
422        tr_bencDictAddInt ( d, "tier", s->tier );
423    }
424}
425
426static void
427addPeers( const tr_torrent * tor,
428          tr_benc *          list )
429{
430    int            i;
431    int            peerCount;
432    tr_peer_stat * peers = tr_torrentPeers( tor, &peerCount );
433
434    tr_bencInitList( list, peerCount );
435
436    for( i = 0; i < peerCount; ++i )
437    {
438        tr_benc *            d = tr_bencListAddDict( list, 14 );
439        const tr_peer_stat * peer = peers + i;
440        tr_bencDictAddStr ( d, "address", peer->addr );
441        tr_bencDictAddStr ( d, "clientName", peer->client );
442        tr_bencDictAddBool( d, "clientIsChoked", peer->clientIsChoked );
443        tr_bencDictAddBool( d, "clientIsInterested", peer->clientIsInterested );
444        tr_bencDictAddStr ( d, "flagStr", peer->flagStr );
445        tr_bencDictAddBool( d, "isDownloadingFrom", peer->isDownloadingFrom );
446        tr_bencDictAddBool( d, "isEncrypted", peer->isEncrypted );
447        tr_bencDictAddBool( d, "isIncoming", peer->isIncoming );
448        tr_bencDictAddBool( d, "isUploadingTo", peer->isUploadingTo );
449        tr_bencDictAddBool( d, "isUTP", peer->isUTP );
450        tr_bencDictAddBool( d, "peerIsChoked", peer->peerIsChoked );
451        tr_bencDictAddBool( d, "peerIsInterested", peer->peerIsInterested );
452        tr_bencDictAddInt ( d, "port", peer->port );
453        tr_bencDictAddReal( d, "progress", peer->progress );
454        tr_bencDictAddInt ( d, "rateToClient", toSpeedBytes( peer->rateToClient_KBps ) );
455        tr_bencDictAddInt ( d, "rateToPeer", toSpeedBytes( peer->rateToPeer_KBps ) );
456    }
457
458    tr_torrentPeersFree( peers, peerCount );
459}
460
461/* faster-than-strcmp() optimization. This is kind of clumsy,
462   but addField() was in the profiler's top 10 list, and this
463   makes it 4x faster... */
464#define tr_streq(a,alen,b) ((alen+1==sizeof(b)) && !memcmp(a,b,alen))
465
466static void
467addField( const tr_torrent * tor, tr_benc * d, const char * key )
468{
469    const tr_info * inf = tr_torrentInfo( tor );
470    const tr_stat * st = tr_torrentStat( (tr_torrent*)tor );
471    const size_t keylen = strlen( key );
472
473    if( tr_streq( key, keylen, "activityDate" ) )
474        tr_bencDictAddInt( d, key, st->activityDate );
475    else if( tr_streq( key, keylen, "addedDate" ) )
476        tr_bencDictAddInt( d, key, st->addedDate );
477    else if( tr_streq( key, keylen, "bandwidthPriority" ) )
478        tr_bencDictAddInt( d, key, tr_torrentGetPriority( tor ) );
479    else if( tr_streq( key, keylen, "comment" ) )
480        tr_bencDictAddStr( d, key, inf->comment ? inf->comment : "" );
481    else if( tr_streq( key, keylen, "corruptEver" ) )
482        tr_bencDictAddInt( d, key, st->corruptEver );
483    else if( tr_streq( key, keylen, "creator" ) )
484        tr_bencDictAddStr( d, key, inf->creator ? inf->creator : "" );
485    else if( tr_streq( key, keylen, "dateCreated" ) )
486        tr_bencDictAddInt( d, key, inf->dateCreated );
487    else if( tr_streq( key, keylen, "desiredAvailable" ) )
488        tr_bencDictAddInt( d, key, st->desiredAvailable );
489    else if( tr_streq( key, keylen, "doneDate" ) )
490        tr_bencDictAddInt( d, key, st->doneDate );
491    else if( tr_streq( key, keylen, "downloadDir" ) )
492        tr_bencDictAddStr( d, key, tr_torrentGetDownloadDir( tor ) );
493    else if( tr_streq( key, keylen, "downloadedEver" ) )
494        tr_bencDictAddInt( d, key, st->downloadedEver );
495    else if( tr_streq( key, keylen, "downloadLimit" ) )
496        tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit_KBps( tor, TR_DOWN ) );
497    else if( tr_streq( key, keylen, "downloadLimited" ) )
498        tr_bencDictAddBool( d, key, tr_torrentUsesSpeedLimit( tor, TR_DOWN ) );
499    else if( tr_streq( key, keylen, "error" ) )
500        tr_bencDictAddInt( d, key, st->error );
501    else if( tr_streq( key, keylen, "errorString" ) )
502        tr_bencDictAddStr( d, key, st->errorString );
503    else if( tr_streq( key, keylen, "eta" ) )
504        tr_bencDictAddInt( d, key, st->eta );
505    else if( tr_streq( key, keylen, "files" ) )
506        addFiles( tor, tr_bencDictAddList( d, key, inf->fileCount ) );
507    else if( tr_streq( key, keylen, "fileStats" ) )
508        addFileStats( tor, tr_bencDictAddList( d, key, inf->fileCount ) );
509    else if( tr_streq( key, keylen, "hashString" ) )
510        tr_bencDictAddStr( d, key, tor->info.hashString );
511    else if( tr_streq( key, keylen, "haveUnchecked" ) )
512        tr_bencDictAddInt( d, key, st->haveUnchecked );
513    else if( tr_streq( key, keylen, "haveValid" ) )
514        tr_bencDictAddInt( d, key, st->haveValid );
515    else if( tr_streq( key, keylen, "honorsSessionLimits" ) )
516        tr_bencDictAddBool( d, key, tr_torrentUsesSessionLimits( tor ) );
517    else if( tr_streq( key, keylen, "id" ) )
518        tr_bencDictAddInt( d, key, st->id );
519    else if( tr_streq( key, keylen, "isFinished" ) )
520        tr_bencDictAddBool( d, key, st->finished );
521    else if( tr_streq( key, keylen, "isPrivate" ) )
522        tr_bencDictAddBool( d, key, tr_torrentIsPrivate( tor ) );
523    else if( tr_streq( key, keylen, "leftUntilDone" ) )
524        tr_bencDictAddInt( d, key, st->leftUntilDone );
525    else if( tr_streq( key, keylen, "manualAnnounceTime" ) )
526        tr_bencDictAddInt( d, key, st->manualAnnounceTime );
527    else if( tr_streq( key, keylen, "maxConnectedPeers" ) )
528        tr_bencDictAddInt( d, key,  tr_torrentGetPeerLimit( tor ) );
529    else if( tr_streq( key, keylen, "magnetLink" ) ) {
530        char * str = tr_torrentGetMagnetLink( tor );
531        tr_bencDictAddStr( d, key, str );
532        tr_free( str );
533    }
534    else if( tr_streq( key, keylen, "metadataPercentComplete" ) )
535        tr_bencDictAddReal( d, key, st->metadataPercentComplete );
536    else if( tr_streq( key, keylen, "name" ) )
537        tr_bencDictAddStr( d, key, tr_torrentName( tor ) );
538    else if( tr_streq( key, keylen, "percentDone" ) )
539        tr_bencDictAddReal( d, key, st->percentDone );
540    else if( tr_streq( key, keylen, "peer-limit" ) )
541        tr_bencDictAddInt( d, key, tr_torrentGetPeerLimit( tor ) );
542    else if( tr_streq( key, keylen, "peers" ) )
543        addPeers( tor, tr_bencDictAdd( d, key ) );
544    else if( tr_streq( key, keylen, "peersConnected" ) )
545        tr_bencDictAddInt( d, key, st->peersConnected );
546    else if( tr_streq( key, keylen, "peersFrom" ) )
547    {
548        tr_benc *   tmp = tr_bencDictAddDict( d, key, 6 );
549        const int * f = st->peersFrom;
550        tr_bencDictAddInt( tmp, "fromCache",    f[TR_PEER_FROM_RESUME] );
551        tr_bencDictAddInt( tmp, "fromDht",      f[TR_PEER_FROM_DHT] );
552        tr_bencDictAddInt( tmp, "fromIncoming", f[TR_PEER_FROM_INCOMING] );
553        tr_bencDictAddInt( tmp, "fromLtep",     f[TR_PEER_FROM_LTEP] );
554        tr_bencDictAddInt( tmp, "fromPex",      f[TR_PEER_FROM_PEX] );
555        tr_bencDictAddInt( tmp, "fromTracker",  f[TR_PEER_FROM_TRACKER] );
556    }
557    else if( tr_streq( key, keylen, "peersGettingFromUs" ) )
558        tr_bencDictAddInt( d, key, st->peersGettingFromUs );
559    else if( tr_streq( key, keylen, "peersSendingToUs" ) )
560        tr_bencDictAddInt( d, key, st->peersSendingToUs );
561    else if( tr_streq( key, keylen, "pieces" ) ) {
562        tr_bitfield * bf = tr_cpCreatePieceBitfield( &tor->completion );
563        char * str = tr_base64_encode( bf->bits, bf->byteCount, NULL );
564        tr_bencDictAddStr( d, key, str!=NULL ? str : "" );
565        tr_free( str );
566        tr_bitfieldFree( bf );
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 tr_bool
772findAnnounceUrl( const tr_tracker_info * t, int n, const char * url, int * pos )
773{
774    int i;
775    tr_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    tr_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_urlIsValid( announce, -1 )
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    tr_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    tr_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        tr_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        tr_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            tr_bool            did_connect UNUSED,
1070            tr_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 tr_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, portTested, idle_data );
1103    tr_free( url );
1104    return NULL;
1105}
1106
1107/***
1108****
1109***/
1110
1111static void
1112gotNewBlocklist( tr_session       * session,
1113                 tr_bool            did_connect UNUSED,
1114                 tr_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, 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                    tr_bool            did_connect UNUSED,
1258                    tr_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 tr_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        tr_bool      boolVal;
1333        const char * str;
1334        tr_benc    * l;
1335        tr_ctor    * ctor = tr_ctorNew( session );
1336
1337        /* set the optional arguments */
1338
1339        if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1340            tr_ctorSetDownloadDir( ctor, TR_FORCE, str );
1341
1342        if( tr_bencDictFindBool( args_in, "paused", &boolVal ) )
1343            tr_ctorSetPaused( ctor, TR_FORCE, boolVal );
1344
1345        if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
1346            tr_ctorSetPeerLimit( ctor, TR_FORCE, i );
1347
1348        if( tr_bencDictFindInt( args_in, "bandwidthPriority", &i ) )
1349            tr_ctorSetBandwidthPriority( ctor, i );
1350
1351        if( tr_bencDictFindList( args_in, "files-unwanted", &l ) ) {
1352            tr_file_index_t fileCount;
1353            tr_file_index_t * files = fileListFromList( l, &fileCount );
1354            tr_ctorSetFilesWanted( ctor, files, fileCount, FALSE );
1355            tr_free( files );
1356        }
1357        if( tr_bencDictFindList( args_in, "files-wanted", &l ) ) {
1358            tr_file_index_t fileCount;
1359            tr_file_index_t * files = fileListFromList( l, &fileCount );
1360            tr_ctorSetFilesWanted( ctor, files, fileCount, TRUE );
1361            tr_free( files );
1362        }
1363
1364        if( tr_bencDictFindList( args_in, "priority-low", &l ) ) {
1365            tr_file_index_t fileCount;
1366            tr_file_index_t * files = fileListFromList( l, &fileCount );
1367            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_LOW );
1368            tr_free( files );
1369        }
1370        if( tr_bencDictFindList( args_in, "priority-normal", &l ) ) {
1371            tr_file_index_t fileCount;
1372            tr_file_index_t * files = fileListFromList( l, &fileCount );
1373            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_NORMAL );
1374            tr_free( files );
1375        }
1376        if( tr_bencDictFindList( args_in, "priority-high", &l ) ) {
1377            tr_file_index_t fileCount;
1378            tr_file_index_t * files = fileListFromList( l, &fileCount );
1379            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_HIGH );
1380            tr_free( files );
1381        }
1382
1383        dbgmsg( "torrentAdd: filename is \"%s\"", filename ? filename : "(null)" );
1384
1385        if( isCurlURL( filename ) )
1386        {
1387            struct add_torrent_idle_data * d = tr_new0( struct add_torrent_idle_data, 1 );
1388            d->data = idle_data;
1389            d->ctor = ctor;
1390            tr_webRun( session, filename, NULL, gotMetadataFromURL, d );
1391        }
1392        else
1393        {
1394            char * fname = tr_strstrip( tr_strdup( filename ) );
1395
1396            if( fname == NULL )
1397            {
1398                int len;
1399                char * metainfo = tr_base64_decode( metainfo_base64, -1, &len );
1400                tr_ctorSetMetainfo( ctor, (uint8_t*)metainfo, len );
1401                tr_free( metainfo );
1402            }
1403            else if( !strncmp( fname, "magnet:?", 8 ) )
1404            {
1405                tr_ctorSetMetainfoFromMagnetLink( ctor, fname );
1406            }
1407            else
1408            {
1409                tr_ctorSetMetainfoFromFile( ctor, fname );
1410            }
1411
1412            addTorrentImpl( idle_data, ctor );
1413
1414            tr_free( fname );
1415        }
1416
1417    }
1418
1419    return NULL;
1420}
1421
1422/***
1423****
1424***/
1425
1426static const char*
1427sessionSet( tr_session               * session,
1428            tr_benc                  * args_in,
1429            tr_benc                  * args_out UNUSED,
1430            struct tr_rpc_idle_data  * idle_data UNUSED )
1431{
1432    int64_t      i;
1433    double       d;
1434    tr_bool      boolVal;
1435    const char * str;
1436
1437    assert( idle_data == NULL );
1438
1439    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, &i ) )
1440        tr_sessionSetCacheLimit_MB( session, i );
1441    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_UP_KBps, &i ) )
1442        tr_sessionSetAltSpeed_KBps( session, TR_UP, i );
1443    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, &i ) )
1444        tr_sessionSetAltSpeed_KBps( session, TR_DOWN, i );
1445    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_ENABLED, &boolVal ) )
1446        tr_sessionUseAltSpeed( session, boolVal );
1447    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &i ) )
1448        tr_sessionSetAltSpeedBegin( session, i );
1449    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_END, &i ) )
1450        tr_sessionSetAltSpeedEnd( session, i );
1451    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &i ) )
1452        tr_sessionSetAltSpeedDay( session, i );
1453    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &boolVal ) )
1454        tr_sessionUseAltSpeedTime( session, boolVal );
1455    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_BLOCKLIST_ENABLED, &boolVal ) )
1456        tr_blocklistSetEnabled( session, boolVal );
1457    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_BLOCKLIST_URL, &str ) )
1458        tr_blocklistSetURL( session, str );
1459    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1460        tr_sessionSetDownloadDir( session, str );
1461    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_INCOMPLETE_DIR, &str ) )
1462        tr_sessionSetIncompleteDir( session, str );
1463    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, &boolVal ) )
1464        tr_sessionSetIncompleteDirEnabled( session, boolVal );
1465    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &i ) )
1466        tr_sessionSetPeerLimit( session, i );
1467    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i ) )
1468        tr_sessionSetPeerLimitPerTorrent( session, i );
1469    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) )
1470        tr_sessionSetPexEnabled( session, boolVal );
1471    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DHT_ENABLED, &boolVal ) )
1472        tr_sessionSetDHTEnabled( session, boolVal );
1473    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_UTP_ENABLED, &boolVal ) )
1474        tr_sessionSetUTPEnabled( session, boolVal );
1475    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_LPD_ENABLED, &boolVal ) )
1476        tr_sessionSetLPDEnabled( session, boolVal );
1477    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, &boolVal ) )
1478        tr_sessionSetPeerPortRandomOnStart( session, boolVal );
1479    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_PORT, &i ) )
1480        tr_sessionSetPeerPort( session, i );
1481    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) )
1482        tr_sessionSetPortForwardingEnabled( session, boolVal );
1483    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_RENAME_PARTIAL_FILES, &boolVal ) )
1484        tr_sessionSetIncompleteFileNamingEnabled( session, boolVal );
1485    if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
1486        tr_sessionSetRatioLimit( session, d );
1487    if( tr_bencDictFindBool( args_in, "seedRatioLimited", &boolVal ) )
1488        tr_sessionSetRatioLimited( session, boolVal );
1489    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_IDLE_LIMIT, &i ) )
1490        tr_sessionSetIdleLimit( session, i );
1491    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, &boolVal ) )
1492        tr_sessionSetIdleLimited( session, boolVal );
1493    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_START, &boolVal ) )
1494        tr_sessionSetPaused( session, !boolVal );
1495    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, &str ) )
1496        tr_sessionSetTorrentDoneScript( session, str );
1497    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, &boolVal ) )
1498        tr_sessionSetTorrentDoneScriptEnabled( session, boolVal );
1499    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_TRASH_ORIGINAL, &boolVal ) )
1500        tr_sessionSetDeleteSource( session, boolVal );
1501    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_DSPEED_KBps, &i ) )
1502        tr_sessionSetSpeedLimit_KBps( session, TR_DOWN, i );
1503    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DSPEED_ENABLED, &boolVal ) )
1504        tr_sessionLimitSpeed( session, TR_DOWN, boolVal );
1505    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_USPEED_KBps, &i ) )
1506        tr_sessionSetSpeedLimit_KBps( session, TR_UP, i );
1507    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_USPEED_ENABLED, &boolVal ) )
1508        tr_sessionLimitSpeed( session, TR_UP, boolVal );
1509    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_ENCRYPTION, &str ) ) {
1510        if( !strcmp( str, "required" ) )
1511            tr_sessionSetEncryption( session, TR_ENCRYPTION_REQUIRED );
1512        else if( !strcmp( str, "tolerated" ) )
1513            tr_sessionSetEncryption( session, TR_CLEAR_PREFERRED );
1514        else
1515            tr_sessionSetEncryption( session, TR_ENCRYPTION_PREFERRED );
1516    }
1517
1518    notify( session, TR_RPC_SESSION_CHANGED, NULL );
1519
1520    return NULL;
1521}
1522
1523static const char*
1524sessionStats( tr_session               * session,
1525              tr_benc                  * args_in UNUSED,
1526              tr_benc                  * args_out,
1527              struct tr_rpc_idle_data  * idle_data UNUSED )
1528{
1529    int running = 0;
1530    int total = 0;
1531    tr_benc * d;
1532    tr_session_stats currentStats = { 0.0f, 0, 0, 0, 0, 0 };
1533    tr_session_stats cumulativeStats = { 0.0f, 0, 0, 0, 0, 0 };
1534    tr_torrent * tor = NULL;
1535
1536    assert( idle_data == NULL );
1537
1538    while(( tor = tr_torrentNext( session, tor ))) {
1539        ++total;
1540        if( tor->isRunning )
1541            ++running;
1542    }
1543
1544    tr_sessionGetStats( session, &currentStats );
1545    tr_sessionGetCumulativeStats( session, &cumulativeStats );
1546
1547    tr_bencDictAddInt ( args_out, "activeTorrentCount", running );
1548    tr_bencDictAddReal( args_out, "downloadSpeed", tr_sessionGetPieceSpeed_Bps( session, TR_DOWN ) );
1549    tr_bencDictAddInt ( args_out, "pausedTorrentCount", total - running );
1550    tr_bencDictAddInt ( args_out, "torrentCount", total );
1551    tr_bencDictAddReal( args_out, "uploadSpeed", tr_sessionGetPieceSpeed_Bps( session, TR_UP ) );
1552
1553    d = tr_bencDictAddDict( args_out, "cumulative-stats", 5 );
1554    tr_bencDictAddInt( d, "downloadedBytes", cumulativeStats.downloadedBytes );
1555    tr_bencDictAddInt( d, "filesAdded", cumulativeStats.filesAdded );
1556    tr_bencDictAddInt( d, "secondsActive", cumulativeStats.secondsActive );
1557    tr_bencDictAddInt( d, "sessionCount", cumulativeStats.sessionCount );
1558    tr_bencDictAddInt( d, "uploadedBytes", cumulativeStats.uploadedBytes );
1559
1560    d = tr_bencDictAddDict( args_out, "current-stats", 5 );
1561    tr_bencDictAddInt( d, "downloadedBytes", currentStats.downloadedBytes );
1562    tr_bencDictAddInt( d, "filesAdded", currentStats.filesAdded );
1563    tr_bencDictAddInt( d, "secondsActive", currentStats.secondsActive );
1564    tr_bencDictAddInt( d, "sessionCount", currentStats.sessionCount );
1565    tr_bencDictAddInt( d, "uploadedBytes", currentStats.uploadedBytes );
1566
1567    return NULL;
1568}
1569
1570static const char*
1571sessionGet( tr_session               * s,
1572            tr_benc                  * args_in UNUSED,
1573            tr_benc                  * args_out,
1574            struct tr_rpc_idle_data  * idle_data UNUSED )
1575{
1576    const char * str;
1577    tr_benc * d = args_out;
1578
1579    assert( idle_data == NULL );
1580    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP_KBps, tr_sessionGetAltSpeed_KBps(s,TR_UP) );
1581    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, tr_sessionGetAltSpeed_KBps(s,TR_DOWN) );
1582    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED, tr_sessionUsesAltSpeed(s) );
1583    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, tr_sessionGetAltSpeedBegin(s) );
1584    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,tr_sessionGetAltSpeedEnd(s) );
1585    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,tr_sessionGetAltSpeedDay(s) );
1586    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, tr_sessionUsesAltSpeedTime(s) );
1587    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, tr_blocklistIsEnabled( s ) );
1588    tr_bencDictAddStr ( d, TR_PREFS_KEY_BLOCKLIST_URL, tr_blocklistGetURL( s ) );
1589    tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, tr_sessionGetCacheLimit_MB( s ) );
1590    tr_bencDictAddInt ( d, "blocklist-size", tr_blocklistGetRuleCount( s ) );
1591    tr_bencDictAddStr ( d, "config-dir", tr_sessionGetConfigDir( s ) );
1592    tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR, tr_sessionGetDownloadDir( s ) );
1593    tr_bencDictAddInt ( d, "download-dir-free-space",  tr_sessionGetDownloadDirFreeSpace( s ) );
1594    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, tr_sessionGetPeerLimit( s ) );
1595    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT, tr_sessionGetPeerLimitPerTorrent( s ) );
1596    tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR, tr_sessionGetIncompleteDir( s ) );
1597    tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, tr_sessionIsIncompleteDirEnabled( s ) );
1598    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED, tr_sessionIsPexEnabled( s ) );
1599    tr_bencDictAddBool( d, TR_PREFS_KEY_UTP_ENABLED, tr_sessionIsUTPEnabled( s ) );
1600    tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED, tr_sessionIsDHTEnabled( s ) );
1601    tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED, tr_sessionIsLPDEnabled( s ) );
1602    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT, tr_sessionGetPeerPort( s ) );
1603    tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, tr_sessionGetPeerPortRandomOnStart( s ) );
1604    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) );
1605    tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES, tr_sessionIsIncompleteFileNamingEnabled( s ) );
1606    tr_bencDictAddInt ( d, "rpc-version", RPC_VERSION );
1607    tr_bencDictAddInt ( d, "rpc-version-minimum", RPC_VERSION_MIN );
1608    tr_bencDictAddReal( d, "seedRatioLimit", tr_sessionGetRatioLimit( s ) );
1609    tr_bencDictAddBool( d, "seedRatioLimited", tr_sessionIsRatioLimited( s ) );
1610    tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT, tr_sessionGetIdleLimit( s ) );
1611    tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, tr_sessionIsIdleLimited( s ) );
1612    tr_bencDictAddBool( d, TR_PREFS_KEY_START, !tr_sessionGetPaused( s ) );
1613    tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL, tr_sessionGetDeleteSource( s ) );
1614    tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_UP ) );
1615    tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_UP ) );
1616    tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_DOWN ) );
1617    tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_DOWN ) );
1618    tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, tr_sessionGetTorrentDoneScript( s ) );
1619    tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, tr_sessionIsTorrentDoneScriptEnabled( s ) );
1620    tr_formatter_get_units( tr_bencDictAddDict( d, "units", 0 ) );
1621    tr_bencDictAddStr ( d, "version", LONG_VERSION_STRING );
1622    switch( tr_sessionGetEncryption( s ) ) {
1623        case TR_CLEAR_PREFERRED: str = "tolerated"; break;
1624        case TR_ENCRYPTION_REQUIRED: str = "required"; break;
1625        default: str = "preferred"; break;
1626    }
1627    tr_bencDictAddStr( d, TR_PREFS_KEY_ENCRYPTION, str );
1628
1629    return NULL;
1630}
1631
1632/***
1633****
1634***/
1635
1636static const char*
1637sessionClose( tr_session               * session,
1638              tr_benc                  * args_in UNUSED,
1639              tr_benc                  * args_out UNUSED,
1640              struct tr_rpc_idle_data  * idle_data UNUSED )
1641{
1642    notify( session, TR_RPC_SESSION_CLOSE, NULL );
1643    return NULL;
1644}
1645
1646/***
1647****
1648***/
1649
1650typedef const char* ( *handler )( tr_session*, tr_benc*, tr_benc*, struct tr_rpc_idle_data * );
1651
1652static struct method
1653{
1654    const char *  name;
1655    tr_bool       immediate;
1656    handler       func;
1657}
1658methods[] =
1659{
1660    { "port-test",             FALSE, portTest            },
1661    { "blocklist-update",      FALSE, blocklistUpdate     },
1662    { "session-close",         TRUE,  sessionClose        },
1663    { "session-get",           TRUE,  sessionGet          },
1664    { "session-set",           TRUE,  sessionSet          },
1665    { "session-stats",         TRUE,  sessionStats        },
1666    { "torrent-add",           FALSE, torrentAdd          },
1667    { "torrent-get",           TRUE,  torrentGet          },
1668    { "torrent-remove",        TRUE,  torrentRemove       },
1669    { "torrent-set",           TRUE,  torrentSet          },
1670    { "torrent-set-location",  TRUE,  torrentSetLocation  },
1671    { "torrent-start",         TRUE,  torrentStart        },
1672    { "torrent-stop",          TRUE,  torrentStop         },
1673    { "torrent-verify",        TRUE,  torrentVerify       },
1674    { "torrent-reannounce",    TRUE,  torrentReannounce   }
1675};
1676
1677static void
1678noop_response_callback( tr_session       * session UNUSED,
1679                        struct evbuffer  * response UNUSED,
1680                        void             * user_data UNUSED )
1681{
1682}
1683
1684static void
1685request_exec( tr_session             * session,
1686              tr_benc                * request,
1687              tr_rpc_response_func     callback,
1688              void                   * callback_user_data )
1689{
1690    int i;
1691    const char * str;
1692    tr_benc * args_in = tr_bencDictFind( request, "arguments" );
1693    const char * result = NULL;
1694
1695    if( callback == NULL )
1696        callback = noop_response_callback;
1697
1698    /* parse the request */
1699    if( !tr_bencDictFindStr( request, "method", &str ) )
1700        result = "no method name";
1701    else {
1702        const int n = TR_N_ELEMENTS( methods );
1703        for( i = 0; i < n; ++i )
1704            if( !strcmp( str, methods[i].name ) )
1705                break;
1706        if( i ==n )
1707            result = "method name not recognized";
1708    }
1709
1710    /* if we couldn't figure out which method to use, return an error */
1711    if( result != NULL )
1712    {
1713        int64_t tag;
1714        tr_benc response;
1715        struct evbuffer * buf = evbuffer_new( );
1716
1717        tr_bencInitDict( &response, 3 );
1718        tr_bencDictAddDict( &response, "arguments", 0 );
1719        tr_bencDictAddStr( &response, "result", result );
1720        if( tr_bencDictFindInt( request, "tag", &tag ) )
1721            tr_bencDictAddInt( &response, "tag", tag );
1722        tr_bencToBuf( &response, TR_FMT_JSON_LEAN, buf );
1723        (*callback)( session, buf, callback_user_data );
1724
1725        evbuffer_free( buf );
1726        tr_bencFree( &response );
1727    }
1728    else if( methods[i].immediate )
1729    {
1730        int64_t tag;
1731        tr_benc response;
1732        tr_benc * args_out;
1733        struct evbuffer * buf = evbuffer_new( );
1734
1735        tr_bencInitDict( &response, 3 );
1736        args_out = tr_bencDictAddDict( &response, "arguments", 0 );
1737        result = (*methods[i].func)( session, args_in, args_out, NULL );
1738        if( result == NULL )
1739            result = "success";
1740        tr_bencDictAddStr( &response, "result", result );
1741        if( tr_bencDictFindInt( request, "tag", &tag ) )
1742            tr_bencDictAddInt( &response, "tag", tag );
1743        tr_bencToBuf( &response, TR_FMT_JSON_LEAN, buf );
1744        (*callback)( session, buf, callback_user_data );
1745
1746        evbuffer_free( buf );
1747        tr_bencFree( &response );
1748    }
1749    else
1750    {
1751        int64_t tag;
1752        struct tr_rpc_idle_data * data = tr_new0( struct tr_rpc_idle_data, 1 );
1753        data->session = session;
1754        data->response = tr_new0( tr_benc, 1 );
1755        tr_bencInitDict( data->response, 3 );
1756        if( tr_bencDictFindInt( request, "tag", &tag ) )
1757            tr_bencDictAddInt( data->response, "tag", tag );
1758        data->args_out = tr_bencDictAddDict( data->response, "arguments", 0 );
1759        data->callback = callback;
1760        data->callback_user_data = callback_user_data;
1761        (*methods[i].func)( session, args_in, data->args_out, data );
1762    }
1763}
1764
1765void
1766tr_rpc_request_exec_json( tr_session            * session,
1767                          const void            * request_json,
1768                          int                     request_len,
1769                          tr_rpc_response_func    callback,
1770                          void                  * callback_user_data )
1771{
1772    tr_benc top;
1773    int have_content;
1774
1775    if( request_len < 0 )
1776        request_len = strlen( request_json );
1777
1778    have_content = !tr_jsonParse( "rpc", request_json, request_len, &top, NULL );
1779    request_exec( session, have_content ? &top : NULL, callback, callback_user_data );
1780
1781    if( have_content )
1782        tr_bencFree( &top );
1783}
1784
1785/**
1786 * Munge the URI into a usable form.
1787 *
1788 * We have very loose typing on this to make the URIs as simple as possible:
1789 * - anything not a 'tag' or 'method' is automatically in 'arguments'
1790 * - values that are all-digits are numbers
1791 * - values that are all-digits or commas are number lists
1792 * - all other values are strings
1793 */
1794void
1795tr_rpc_parse_list_str( tr_benc     * setme,
1796                       const char  * str,
1797                       int           len )
1798
1799{
1800    int valueCount;
1801    int * values = tr_parseNumberRange( str, len, &valueCount );
1802
1803    if( valueCount == 0 )
1804        tr_bencInitStr( setme, str, len );
1805    else if( valueCount == 1 )
1806        tr_bencInitInt( setme, values[0] );
1807    else {
1808        int i;
1809        tr_bencInitList( setme, valueCount );
1810        for( i=0; i<valueCount; ++i )
1811            tr_bencListAddInt( setme, values[i] );
1812    }
1813
1814    tr_free( values );
1815}
1816
1817void
1818tr_rpc_request_exec_uri( tr_session           * session,
1819                         const void           * request_uri,
1820                         int                    request_len,
1821                         tr_rpc_response_func   callback,
1822                         void                 * callback_user_data )
1823{
1824    tr_benc      top, * args;
1825    char *       request = tr_strndup( request_uri, request_len );
1826    const char * pch;
1827
1828    tr_bencInitDict( &top, 3 );
1829    args = tr_bencDictAddDict( &top, "arguments", 0 );
1830
1831    pch = strchr( request, '?' );
1832    if( !pch ) pch = request;
1833    while( pch )
1834    {
1835        const char * delim = strchr( pch, '=' );
1836        const char * next = strchr( pch, '&' );
1837        if( delim )
1838        {
1839            char *    key = tr_strndup( pch, delim - pch );
1840            int       isArg = strcmp( key, "method" ) && strcmp( key, "tag" );
1841            tr_benc * parent = isArg ? args : &top;
1842            tr_rpc_parse_list_str( tr_bencDictAdd( parent, key ),
1843                                  delim + 1,
1844                                  next ? (size_t)(
1845                                       next -
1846                                      ( delim + 1 ) ) : strlen( delim + 1 ) );
1847            tr_free( key );
1848        }
1849        pch = next ? next + 1 : NULL;
1850    }
1851
1852    request_exec( session, &top, callback, callback_user_data );
1853
1854    /* cleanup */
1855    tr_bencFree( &top );
1856    tr_free( request );
1857}
Note: See TracBrowser for help on using the repository browser.