source: trunk/libtransmission/rpcimpl.c @ 11635

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

(trunk libT) Fix gzopen64 implicit declaration warning on some systems. (ijuxda ba5f6a66ad949b410464)

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