source: trunk/libtransmission/rpcimpl.c @ 12168

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

(trunk) #4081 "Add 'cookieString' argument 'torrent-add' method in RPC" -- done.

  • 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 12168 2011-03-15 04:22:47Z 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, 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, 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        tr_benc    * l;
1334        const char * str;
1335        const char * cookies = NULL;
1336        tr_ctor    * ctor = tr_ctorNew( session );
1337
1338        /* set the optional arguments */
1339
1340        tr_bencDictFindStr( args_in, "cookies", &cookies );
1341
1342        if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1343            tr_ctorSetDownloadDir( ctor, TR_FORCE, str );
1344
1345        if( tr_bencDictFindBool( args_in, "paused", &boolVal ) )
1346            tr_ctorSetPaused( ctor, TR_FORCE, boolVal );
1347
1348        if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
1349            tr_ctorSetPeerLimit( ctor, TR_FORCE, i );
1350
1351        if( tr_bencDictFindInt( args_in, "bandwidthPriority", &i ) )
1352            tr_ctorSetBandwidthPriority( ctor, i );
1353
1354        if( tr_bencDictFindList( args_in, "files-unwanted", &l ) ) {
1355            tr_file_index_t fileCount;
1356            tr_file_index_t * files = fileListFromList( l, &fileCount );
1357            tr_ctorSetFilesWanted( ctor, files, fileCount, FALSE );
1358            tr_free( files );
1359        }
1360        if( tr_bencDictFindList( args_in, "files-wanted", &l ) ) {
1361            tr_file_index_t fileCount;
1362            tr_file_index_t * files = fileListFromList( l, &fileCount );
1363            tr_ctorSetFilesWanted( ctor, files, fileCount, TRUE );
1364            tr_free( files );
1365        }
1366
1367        if( tr_bencDictFindList( args_in, "priority-low", &l ) ) {
1368            tr_file_index_t fileCount;
1369            tr_file_index_t * files = fileListFromList( l, &fileCount );
1370            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_LOW );
1371            tr_free( files );
1372        }
1373        if( tr_bencDictFindList( args_in, "priority-normal", &l ) ) {
1374            tr_file_index_t fileCount;
1375            tr_file_index_t * files = fileListFromList( l, &fileCount );
1376            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_NORMAL );
1377            tr_free( files );
1378        }
1379        if( tr_bencDictFindList( args_in, "priority-high", &l ) ) {
1380            tr_file_index_t fileCount;
1381            tr_file_index_t * files = fileListFromList( l, &fileCount );
1382            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_HIGH );
1383            tr_free( files );
1384        }
1385
1386        dbgmsg( "torrentAdd: filename is \"%s\"", filename ? filename : "(null)" );
1387
1388        if( isCurlURL( filename ) )
1389        {
1390            struct add_torrent_idle_data * d = tr_new0( struct add_torrent_idle_data, 1 );
1391            d->data = idle_data;
1392            d->ctor = ctor;
1393            tr_webRun( session, filename, NULL, cookies, gotMetadataFromURL, d );
1394        }
1395        else
1396        {
1397            char * fname = tr_strstrip( tr_strdup( filename ) );
1398
1399            if( fname == NULL )
1400            {
1401                int len;
1402                char * metainfo = tr_base64_decode( metainfo_base64, -1, &len );
1403                tr_ctorSetMetainfo( ctor, (uint8_t*)metainfo, len );
1404                tr_free( metainfo );
1405            }
1406            else if( !strncmp( fname, "magnet:?", 8 ) )
1407            {
1408                tr_ctorSetMetainfoFromMagnetLink( ctor, fname );
1409            }
1410            else
1411            {
1412                tr_ctorSetMetainfoFromFile( ctor, fname );
1413            }
1414
1415            addTorrentImpl( idle_data, ctor );
1416
1417            tr_free( fname );
1418        }
1419
1420    }
1421
1422    return NULL;
1423}
1424
1425/***
1426****
1427***/
1428
1429static const char*
1430sessionSet( tr_session               * session,
1431            tr_benc                  * args_in,
1432            tr_benc                  * args_out UNUSED,
1433            struct tr_rpc_idle_data  * idle_data UNUSED )
1434{
1435    int64_t      i;
1436    double       d;
1437    tr_bool      boolVal;
1438    const char * str;
1439
1440    assert( idle_data == NULL );
1441
1442    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, &i ) )
1443        tr_sessionSetCacheLimit_MB( session, i );
1444    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_UP_KBps, &i ) )
1445        tr_sessionSetAltSpeed_KBps( session, TR_UP, i );
1446    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, &i ) )
1447        tr_sessionSetAltSpeed_KBps( session, TR_DOWN, i );
1448    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_ENABLED, &boolVal ) )
1449        tr_sessionUseAltSpeed( session, boolVal );
1450    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &i ) )
1451        tr_sessionSetAltSpeedBegin( session, i );
1452    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_END, &i ) )
1453        tr_sessionSetAltSpeedEnd( session, i );
1454    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &i ) )
1455        tr_sessionSetAltSpeedDay( session, i );
1456    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &boolVal ) )
1457        tr_sessionUseAltSpeedTime( session, boolVal );
1458    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_BLOCKLIST_ENABLED, &boolVal ) )
1459        tr_blocklistSetEnabled( session, boolVal );
1460    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_BLOCKLIST_URL, &str ) )
1461        tr_blocklistSetURL( session, str );
1462    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1463        tr_sessionSetDownloadDir( session, str );
1464    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_INCOMPLETE_DIR, &str ) )
1465        tr_sessionSetIncompleteDir( session, str );
1466    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, &boolVal ) )
1467        tr_sessionSetIncompleteDirEnabled( session, boolVal );
1468    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &i ) )
1469        tr_sessionSetPeerLimit( session, i );
1470    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i ) )
1471        tr_sessionSetPeerLimitPerTorrent( session, i );
1472    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) )
1473        tr_sessionSetPexEnabled( session, boolVal );
1474    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DHT_ENABLED, &boolVal ) )
1475        tr_sessionSetDHTEnabled( session, boolVal );
1476    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_UTP_ENABLED, &boolVal ) )
1477        tr_sessionSetUTPEnabled( session, boolVal );
1478    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_LPD_ENABLED, &boolVal ) )
1479        tr_sessionSetLPDEnabled( session, boolVal );
1480    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, &boolVal ) )
1481        tr_sessionSetPeerPortRandomOnStart( session, boolVal );
1482    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_PORT, &i ) )
1483        tr_sessionSetPeerPort( session, i );
1484    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) )
1485        tr_sessionSetPortForwardingEnabled( session, boolVal );
1486    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_RENAME_PARTIAL_FILES, &boolVal ) )
1487        tr_sessionSetIncompleteFileNamingEnabled( session, boolVal );
1488    if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
1489        tr_sessionSetRatioLimit( session, d );
1490    if( tr_bencDictFindBool( args_in, "seedRatioLimited", &boolVal ) )
1491        tr_sessionSetRatioLimited( session, boolVal );
1492    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_IDLE_LIMIT, &i ) )
1493        tr_sessionSetIdleLimit( session, i );
1494    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, &boolVal ) )
1495        tr_sessionSetIdleLimited( session, boolVal );
1496    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_START, &boolVal ) )
1497        tr_sessionSetPaused( session, !boolVal );
1498    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, &str ) )
1499        tr_sessionSetTorrentDoneScript( session, str );
1500    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, &boolVal ) )
1501        tr_sessionSetTorrentDoneScriptEnabled( session, boolVal );
1502    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_TRASH_ORIGINAL, &boolVal ) )
1503        tr_sessionSetDeleteSource( session, boolVal );
1504    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_DSPEED_KBps, &i ) )
1505        tr_sessionSetSpeedLimit_KBps( session, TR_DOWN, i );
1506    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DSPEED_ENABLED, &boolVal ) )
1507        tr_sessionLimitSpeed( session, TR_DOWN, boolVal );
1508    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_USPEED_KBps, &i ) )
1509        tr_sessionSetSpeedLimit_KBps( session, TR_UP, i );
1510    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_USPEED_ENABLED, &boolVal ) )
1511        tr_sessionLimitSpeed( session, TR_UP, boolVal );
1512    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_ENCRYPTION, &str ) ) {
1513        if( !strcmp( str, "required" ) )
1514            tr_sessionSetEncryption( session, TR_ENCRYPTION_REQUIRED );
1515        else if( !strcmp( str, "tolerated" ) )
1516            tr_sessionSetEncryption( session, TR_CLEAR_PREFERRED );
1517        else
1518            tr_sessionSetEncryption( session, TR_ENCRYPTION_PREFERRED );
1519    }
1520
1521    notify( session, TR_RPC_SESSION_CHANGED, NULL );
1522
1523    return NULL;
1524}
1525
1526static const char*
1527sessionStats( tr_session               * session,
1528              tr_benc                  * args_in UNUSED,
1529              tr_benc                  * args_out,
1530              struct tr_rpc_idle_data  * idle_data UNUSED )
1531{
1532    int running = 0;
1533    int total = 0;
1534    tr_benc * d;
1535    tr_session_stats currentStats = { 0.0f, 0, 0, 0, 0, 0 };
1536    tr_session_stats cumulativeStats = { 0.0f, 0, 0, 0, 0, 0 };
1537    tr_torrent * tor = NULL;
1538
1539    assert( idle_data == NULL );
1540
1541    while(( tor = tr_torrentNext( session, tor ))) {
1542        ++total;
1543        if( tor->isRunning )
1544            ++running;
1545    }
1546
1547    tr_sessionGetStats( session, &currentStats );
1548    tr_sessionGetCumulativeStats( session, &cumulativeStats );
1549
1550    tr_bencDictAddInt ( args_out, "activeTorrentCount", running );
1551    tr_bencDictAddReal( args_out, "downloadSpeed", tr_sessionGetPieceSpeed_Bps( session, TR_DOWN ) );
1552    tr_bencDictAddInt ( args_out, "pausedTorrentCount", total - running );
1553    tr_bencDictAddInt ( args_out, "torrentCount", total );
1554    tr_bencDictAddReal( args_out, "uploadSpeed", tr_sessionGetPieceSpeed_Bps( session, TR_UP ) );
1555
1556    d = tr_bencDictAddDict( args_out, "cumulative-stats", 5 );
1557    tr_bencDictAddInt( d, "downloadedBytes", cumulativeStats.downloadedBytes );
1558    tr_bencDictAddInt( d, "filesAdded", cumulativeStats.filesAdded );
1559    tr_bencDictAddInt( d, "secondsActive", cumulativeStats.secondsActive );
1560    tr_bencDictAddInt( d, "sessionCount", cumulativeStats.sessionCount );
1561    tr_bencDictAddInt( d, "uploadedBytes", cumulativeStats.uploadedBytes );
1562
1563    d = tr_bencDictAddDict( args_out, "current-stats", 5 );
1564    tr_bencDictAddInt( d, "downloadedBytes", currentStats.downloadedBytes );
1565    tr_bencDictAddInt( d, "filesAdded", currentStats.filesAdded );
1566    tr_bencDictAddInt( d, "secondsActive", currentStats.secondsActive );
1567    tr_bencDictAddInt( d, "sessionCount", currentStats.sessionCount );
1568    tr_bencDictAddInt( d, "uploadedBytes", currentStats.uploadedBytes );
1569
1570    return NULL;
1571}
1572
1573static const char*
1574sessionGet( tr_session               * s,
1575            tr_benc                  * args_in UNUSED,
1576            tr_benc                  * args_out,
1577            struct tr_rpc_idle_data  * idle_data UNUSED )
1578{
1579    const char * str;
1580    tr_benc * d = args_out;
1581
1582    assert( idle_data == NULL );
1583    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP_KBps, tr_sessionGetAltSpeed_KBps(s,TR_UP) );
1584    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, tr_sessionGetAltSpeed_KBps(s,TR_DOWN) );
1585    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED, tr_sessionUsesAltSpeed(s) );
1586    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, tr_sessionGetAltSpeedBegin(s) );
1587    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,tr_sessionGetAltSpeedEnd(s) );
1588    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,tr_sessionGetAltSpeedDay(s) );
1589    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, tr_sessionUsesAltSpeedTime(s) );
1590    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, tr_blocklistIsEnabled( s ) );
1591    tr_bencDictAddStr ( d, TR_PREFS_KEY_BLOCKLIST_URL, tr_blocklistGetURL( s ) );
1592    tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, tr_sessionGetCacheLimit_MB( s ) );
1593    tr_bencDictAddInt ( d, "blocklist-size", tr_blocklistGetRuleCount( s ) );
1594    tr_bencDictAddStr ( d, "config-dir", tr_sessionGetConfigDir( s ) );
1595    tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR, tr_sessionGetDownloadDir( s ) );
1596    tr_bencDictAddInt ( d, "download-dir-free-space",  tr_sessionGetDownloadDirFreeSpace( s ) );
1597    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, tr_sessionGetPeerLimit( s ) );
1598    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT, tr_sessionGetPeerLimitPerTorrent( s ) );
1599    tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR, tr_sessionGetIncompleteDir( s ) );
1600    tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, tr_sessionIsIncompleteDirEnabled( s ) );
1601    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED, tr_sessionIsPexEnabled( s ) );
1602    tr_bencDictAddBool( d, TR_PREFS_KEY_UTP_ENABLED, tr_sessionIsUTPEnabled( s ) );
1603    tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED, tr_sessionIsDHTEnabled( s ) );
1604    tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED, tr_sessionIsLPDEnabled( s ) );
1605    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT, tr_sessionGetPeerPort( s ) );
1606    tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, tr_sessionGetPeerPortRandomOnStart( s ) );
1607    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) );
1608    tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES, tr_sessionIsIncompleteFileNamingEnabled( s ) );
1609    tr_bencDictAddInt ( d, "rpc-version", RPC_VERSION );
1610    tr_bencDictAddInt ( d, "rpc-version-minimum", RPC_VERSION_MIN );
1611    tr_bencDictAddReal( d, "seedRatioLimit", tr_sessionGetRatioLimit( s ) );
1612    tr_bencDictAddBool( d, "seedRatioLimited", tr_sessionIsRatioLimited( s ) );
1613    tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT, tr_sessionGetIdleLimit( s ) );
1614    tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, tr_sessionIsIdleLimited( s ) );
1615    tr_bencDictAddBool( d, TR_PREFS_KEY_START, !tr_sessionGetPaused( s ) );
1616    tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL, tr_sessionGetDeleteSource( s ) );
1617    tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_UP ) );
1618    tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_UP ) );
1619    tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_DOWN ) );
1620    tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_DOWN ) );
1621    tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, tr_sessionGetTorrentDoneScript( s ) );
1622    tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, tr_sessionIsTorrentDoneScriptEnabled( s ) );
1623    tr_formatter_get_units( tr_bencDictAddDict( d, "units", 0 ) );
1624    tr_bencDictAddStr ( d, "version", LONG_VERSION_STRING );
1625    switch( tr_sessionGetEncryption( s ) ) {
1626        case TR_CLEAR_PREFERRED: str = "tolerated"; break;
1627        case TR_ENCRYPTION_REQUIRED: str = "required"; break;
1628        default: str = "preferred"; break;
1629    }
1630    tr_bencDictAddStr( d, TR_PREFS_KEY_ENCRYPTION, str );
1631
1632    return NULL;
1633}
1634
1635/***
1636****
1637***/
1638
1639static const char*
1640sessionClose( tr_session               * session,
1641              tr_benc                  * args_in UNUSED,
1642              tr_benc                  * args_out UNUSED,
1643              struct tr_rpc_idle_data  * idle_data UNUSED )
1644{
1645    notify( session, TR_RPC_SESSION_CLOSE, NULL );
1646    return NULL;
1647}
1648
1649/***
1650****
1651***/
1652
1653typedef const char* ( *handler )( tr_session*, tr_benc*, tr_benc*, struct tr_rpc_idle_data * );
1654
1655static struct method
1656{
1657    const char *  name;
1658    tr_bool       immediate;
1659    handler       func;
1660}
1661methods[] =
1662{
1663    { "port-test",             FALSE, portTest            },
1664    { "blocklist-update",      FALSE, blocklistUpdate     },
1665    { "session-close",         TRUE,  sessionClose        },
1666    { "session-get",           TRUE,  sessionGet          },
1667    { "session-set",           TRUE,  sessionSet          },
1668    { "session-stats",         TRUE,  sessionStats        },
1669    { "torrent-add",           FALSE, torrentAdd          },
1670    { "torrent-get",           TRUE,  torrentGet          },
1671    { "torrent-remove",        TRUE,  torrentRemove       },
1672    { "torrent-set",           TRUE,  torrentSet          },
1673    { "torrent-set-location",  TRUE,  torrentSetLocation  },
1674    { "torrent-start",         TRUE,  torrentStart        },
1675    { "torrent-stop",          TRUE,  torrentStop         },
1676    { "torrent-verify",        TRUE,  torrentVerify       },
1677    { "torrent-reannounce",    TRUE,  torrentReannounce   }
1678};
1679
1680static void
1681noop_response_callback( tr_session       * session UNUSED,
1682                        struct evbuffer  * response UNUSED,
1683                        void             * user_data UNUSED )
1684{
1685}
1686
1687static void
1688request_exec( tr_session             * session,
1689              tr_benc                * request,
1690              tr_rpc_response_func     callback,
1691              void                   * callback_user_data )
1692{
1693    int i;
1694    const char * str;
1695    tr_benc * args_in = tr_bencDictFind( request, "arguments" );
1696    const char * result = NULL;
1697
1698    if( callback == NULL )
1699        callback = noop_response_callback;
1700
1701    /* parse the request */
1702    if( !tr_bencDictFindStr( request, "method", &str ) )
1703        result = "no method name";
1704    else {
1705        const int n = TR_N_ELEMENTS( methods );
1706        for( i = 0; i < n; ++i )
1707            if( !strcmp( str, methods[i].name ) )
1708                break;
1709        if( i ==n )
1710            result = "method name not recognized";
1711    }
1712
1713    /* if we couldn't figure out which method to use, return an error */
1714    if( result != NULL )
1715    {
1716        int64_t tag;
1717        tr_benc response;
1718        struct evbuffer * buf = evbuffer_new( );
1719
1720        tr_bencInitDict( &response, 3 );
1721        tr_bencDictAddDict( &response, "arguments", 0 );
1722        tr_bencDictAddStr( &response, "result", result );
1723        if( tr_bencDictFindInt( request, "tag", &tag ) )
1724            tr_bencDictAddInt( &response, "tag", tag );
1725        tr_bencToBuf( &response, TR_FMT_JSON_LEAN, buf );
1726        (*callback)( session, buf, callback_user_data );
1727
1728        evbuffer_free( buf );
1729        tr_bencFree( &response );
1730    }
1731    else if( methods[i].immediate )
1732    {
1733        int64_t tag;
1734        tr_benc response;
1735        tr_benc * args_out;
1736        struct evbuffer * buf = evbuffer_new( );
1737
1738        tr_bencInitDict( &response, 3 );
1739        args_out = tr_bencDictAddDict( &response, "arguments", 0 );
1740        result = (*methods[i].func)( session, args_in, args_out, NULL );
1741        if( result == NULL )
1742            result = "success";
1743        tr_bencDictAddStr( &response, "result", result );
1744        if( tr_bencDictFindInt( request, "tag", &tag ) )
1745            tr_bencDictAddInt( &response, "tag", tag );
1746        tr_bencToBuf( &response, TR_FMT_JSON_LEAN, buf );
1747        (*callback)( session, buf, callback_user_data );
1748
1749        evbuffer_free( buf );
1750        tr_bencFree( &response );
1751    }
1752    else
1753    {
1754        int64_t tag;
1755        struct tr_rpc_idle_data * data = tr_new0( struct tr_rpc_idle_data, 1 );
1756        data->session = session;
1757        data->response = tr_new0( tr_benc, 1 );
1758        tr_bencInitDict( data->response, 3 );
1759        if( tr_bencDictFindInt( request, "tag", &tag ) )
1760            tr_bencDictAddInt( data->response, "tag", tag );
1761        data->args_out = tr_bencDictAddDict( data->response, "arguments", 0 );
1762        data->callback = callback;
1763        data->callback_user_data = callback_user_data;
1764        (*methods[i].func)( session, args_in, data->args_out, data );
1765    }
1766}
1767
1768void
1769tr_rpc_request_exec_json( tr_session            * session,
1770                          const void            * request_json,
1771                          int                     request_len,
1772                          tr_rpc_response_func    callback,
1773                          void                  * callback_user_data )
1774{
1775    tr_benc top;
1776    int have_content;
1777
1778    if( request_len < 0 )
1779        request_len = strlen( request_json );
1780
1781    have_content = !tr_jsonParse( "rpc", request_json, request_len, &top, NULL );
1782    request_exec( session, have_content ? &top : NULL, callback, callback_user_data );
1783
1784    if( have_content )
1785        tr_bencFree( &top );
1786}
1787
1788/**
1789 * Munge the URI into a usable form.
1790 *
1791 * We have very loose typing on this to make the URIs as simple as possible:
1792 * - anything not a 'tag' or 'method' is automatically in 'arguments'
1793 * - values that are all-digits are numbers
1794 * - values that are all-digits or commas are number lists
1795 * - all other values are strings
1796 */
1797void
1798tr_rpc_parse_list_str( tr_benc     * setme,
1799                       const char  * str,
1800                       int           len )
1801
1802{
1803    int valueCount;
1804    int * values = tr_parseNumberRange( str, len, &valueCount );
1805
1806    if( valueCount == 0 )
1807        tr_bencInitStr( setme, str, len );
1808    else if( valueCount == 1 )
1809        tr_bencInitInt( setme, values[0] );
1810    else {
1811        int i;
1812        tr_bencInitList( setme, valueCount );
1813        for( i=0; i<valueCount; ++i )
1814            tr_bencListAddInt( setme, values[i] );
1815    }
1816
1817    tr_free( values );
1818}
1819
1820void
1821tr_rpc_request_exec_uri( tr_session           * session,
1822                         const void           * request_uri,
1823                         int                    request_len,
1824                         tr_rpc_response_func   callback,
1825                         void                 * callback_user_data )
1826{
1827    tr_benc      top, * args;
1828    char *       request = tr_strndup( request_uri, request_len );
1829    const char * pch;
1830
1831    tr_bencInitDict( &top, 3 );
1832    args = tr_bencDictAddDict( &top, "arguments", 0 );
1833
1834    pch = strchr( request, '?' );
1835    if( !pch ) pch = request;
1836    while( pch )
1837    {
1838        const char * delim = strchr( pch, '=' );
1839        const char * next = strchr( pch, '&' );
1840        if( delim )
1841        {
1842            char *    key = tr_strndup( pch, delim - pch );
1843            int       isArg = strcmp( key, "method" ) && strcmp( key, "tag" );
1844            tr_benc * parent = isArg ? args : &top;
1845            tr_rpc_parse_list_str( tr_bencDictAdd( parent, key ),
1846                                  delim + 1,
1847                                  next ? (size_t)(
1848                                       next -
1849                                      ( delim + 1 ) ) : strlen( delim + 1 ) );
1850            tr_free( key );
1851        }
1852        pch = next ? next + 1 : NULL;
1853    }
1854
1855    request_exec( session, &top, callback, callback_user_data );
1856
1857    /* cleanup */
1858    tr_bencFree( &top );
1859    tr_free( request );
1860}
Note: See TracBrowser for help on using the repository browser.