source: trunk/libtransmission/rpcimpl.c @ 8168

Last change on this file since 8168 was 8168, checked in by charles, 13 years ago

(trunk libT) fix typo

  • Property svn:keywords set to Date Rev Author Id
File size: 49.6 KB
Line 
1/*
2 * This file Copyright (C) 2008-2009 Charles Kerr <charles@transmissionbt.com>
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 8168 2009-04-06 20:33:50Z charles $
11 */
12
13#include <assert.h>
14#include <ctype.h> /* isdigit */
15#include <stdlib.h> /* strtol */
16#include <string.h> /* strcmp */
17#include <unistd.h> /* unlink */
18
19#include <event.h> /* evbuffer */
20
21#include "transmission.h"
22#include "bencode.h"
23#include "rpcimpl.h"
24#include "json.h"
25#include "session.h"
26#include "stats.h"
27#include "torrent.h"
28#include "completion.h"
29#include "utils.h"
30#include "web.h"
31
32#define RECENTLY_ACTIVE_SECONDS 60
33
34#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) )
35
36#if 0
37#define dbgmsg(fmt, ...) \
38    do { \
39        fprintf( stderr, "%s:%d"#fmt, __FILE__, __LINE__, __VA_ARGS__ ); \
40        fprintf( stderr, "\n" ); \
41    } while( 0 )
42#else
43#define dbgmsg( ... ) \
44    do { \
45        if( tr_deepLoggingIsActive( ) ) \
46            tr_deepLog( __FILE__, __LINE__, "RPC", __VA_ARGS__ ); \
47    } while( 0 )
48#endif
49
50
51/***
52****
53***/
54
55static tr_rpc_callback_status
56notify( tr_session * session,
57        int          type,
58        tr_torrent * tor )
59{
60    tr_rpc_callback_status status = 0;
61
62    if( session->rpc_func )
63        status = session->rpc_func( session, type, tor,
64                                    session->rpc_func_user_data );
65
66    return status;
67}
68
69/***
70****
71***/
72
73/* For functions that can't be immediately executed, like torrentAdd,
74 * this is the callback data used to pass a response to the caller
75 * when the task is complete */
76struct tr_rpc_idle_data
77{
78    tr_session            * session;
79    tr_benc               * response;
80    tr_benc               * args_out;
81    tr_rpc_response_func    callback;
82    void                  * callback_user_data;
83};
84
85static void
86tr_idle_function_done( struct tr_rpc_idle_data * data, const char * result )
87{
88    struct evbuffer * buf = tr_getBuffer( );
89
90    if( result == NULL )
91        result = "success";
92    tr_bencDictAddStr( data->response, "result", result );
93
94    tr_bencSaveAsJSON( data->response, buf );
95    (*data->callback)( data->session, (const char*)EVBUFFER_DATA(buf),
96                       EVBUFFER_LENGTH(buf), data->callback_user_data );
97
98    tr_releaseBuffer( buf );
99    tr_bencFree( data->response );
100    tr_free( data->response );
101    tr_free( data );
102}
103
104/***
105****
106***/
107
108static tr_torrent **
109getTorrents( tr_session * session,
110             tr_benc    * args,
111             int        * setmeCount )
112{
113    int           torrentCount = 0;
114    int64_t       id;
115    tr_torrent ** torrents = NULL;
116    tr_benc *     ids;
117    const char * str;
118
119    if( tr_bencDictFindList( args, "ids", &ids ) )
120    {
121        int       i;
122        const int n = tr_bencListSize( ids );
123
124        torrents = tr_new0( tr_torrent *, n );
125
126        for( i = 0; i < n; ++i )
127        {
128            tr_torrent * tor = NULL;
129            tr_benc *    node = tr_bencListChild( ids, i );
130            const char * str;
131            if( tr_bencGetInt( node, &id ) )
132                tor = tr_torrentFindFromId( session, id );
133            else if( tr_bencGetStr( node, &str ) )
134                tor = tr_torrentFindFromHashString( session, str );
135            if( tor )
136                torrents[torrentCount++] = tor;
137        }
138    }
139    else if( tr_bencDictFindInt( args, "ids", &id )
140           || tr_bencDictFindInt( args, "id", &id ) )
141    {
142        tr_torrent * tor;
143        torrents = tr_new0( tr_torrent *, 1 );
144        if( ( tor = tr_torrentFindFromId( session, id ) ) )
145            torrents[torrentCount++] = tor;
146    }
147    else if( tr_bencDictFindStr( args, "ids", &str ) )
148    {
149        if( !strcmp( str, "recently-active" ) )
150        {
151            tr_torrent * tor = NULL;
152            const time_t now = time( NULL );
153            const time_t window = RECENTLY_ACTIVE_SECONDS;
154            const int n = tr_sessionCountTorrents( session );
155            torrents = tr_new0( tr_torrent *, n );
156            while( ( tor = tr_torrentNext( session, tor ) ) )
157                if( tor->anyDate >= now - window )
158                    torrents[torrentCount++] = tor;
159        }
160    }
161    else /* all of them */
162    {
163        tr_torrent * tor = NULL;
164        const int    n = tr_sessionCountTorrents( session );
165        torrents = tr_new0( tr_torrent *, n );
166        while( ( tor = tr_torrentNext( session, tor ) ) )
167            torrents[torrentCount++] = tor;
168    }
169
170    *setmeCount = torrentCount;
171    return torrents;
172}
173
174static const char*
175torrentStart( tr_session               * session,
176              tr_benc                  * args_in,
177              tr_benc                  * args_out UNUSED,
178              struct tr_rpc_idle_data  * idle_data )
179{
180    int           i, torrentCount;
181    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
182
183    assert( idle_data == NULL );
184
185    for( i = 0; i < torrentCount; ++i )
186    {
187        tr_torrent * tor = torrents[i];
188        tr_torrentStart( tor );
189        notify( session, TR_RPC_TORRENT_STARTED, tor );
190    }
191    tr_free( torrents );
192    return NULL;
193}
194
195static const char*
196torrentStop( tr_session               * session,
197             tr_benc                  * args_in,
198             tr_benc                  * args_out UNUSED,
199             struct tr_rpc_idle_data  * idle_data )
200{
201    int           i, torrentCount;
202    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
203
204    assert( idle_data == NULL );
205
206    for( i = 0; i < torrentCount; ++i )
207    {
208        tr_torrent * tor = torrents[i];
209        tr_torrentStop( tor );
210        notify( session, TR_RPC_TORRENT_STOPPED, tor );
211    }
212    tr_free( torrents );
213    return NULL;
214}
215
216static const char*
217torrentRemove( tr_session               * session,
218               tr_benc                  * args_in,
219               tr_benc                  * args_out UNUSED,
220               struct tr_rpc_idle_data  * idle_data )
221{
222    int i;
223    int torrentCount;
224    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
225
226    assert( idle_data == NULL );
227
228    for( i=0; i<torrentCount; ++i )
229    {
230        tr_torrent * tor = torrents[i];
231        const tr_rpc_callback_status status = notify( session, TR_RPC_TORRENT_REMOVING, tor );
232        tr_bool deleteFlag;
233        if( tr_bencDictFindBool( args_in, "delete-local-data", &deleteFlag ) && deleteFlag )
234            tr_torrentDeleteLocalData( tor, NULL );
235        if( !( status & TR_RPC_NOREMOVE ) )
236            tr_torrentRemove( tor );
237    }
238
239    tr_free( torrents );
240    return NULL;
241}
242
243static const char*
244torrentReannounce( tr_session               * session,
245                   tr_benc                  * args_in,
246                   tr_benc                  * args_out UNUSED,
247                   struct tr_rpc_idle_data  * idle_data )
248{
249    int i, torrentCount;
250    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
251
252    assert( idle_data == NULL );
253
254    for( i=0; i<torrentCount; ++i )
255    {
256        tr_torrent * tor = torrents[i];
257        if( tr_torrentCanManualUpdate( tor ) )
258        {
259            tr_torrentManualUpdate( tor );
260            notify( session, TR_RPC_TORRENT_CHANGED, tor );
261        }
262    }
263
264    tr_free( torrents );
265    return NULL;
266}
267
268static const char*
269torrentVerify( tr_session               * session,
270               tr_benc                  * args_in,
271               tr_benc                  * args_out UNUSED,
272               struct tr_rpc_idle_data  * idle_data )
273{
274    int           i, torrentCount;
275    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
276
277    assert( idle_data == NULL );
278
279    for( i = 0; i < torrentCount; ++i )
280    {
281        tr_torrent * tor = torrents[i];
282        tr_torrentVerify( tor );
283        notify( session, TR_RPC_TORRENT_CHANGED, tor );
284    }
285
286    tr_free( torrents );
287    return NULL;
288}
289
290/***
291****
292***/
293
294static void
295addFileStats( const tr_torrent * tor, tr_benc * list )
296{
297    tr_file_index_t i;
298    tr_file_index_t n;
299    const tr_info * info = tr_torrentInfo( tor );
300    tr_file_stat * files = tr_torrentFiles( tor, &n );
301
302    for( i = 0; i < info->fileCount; ++i )
303    {
304        const tr_file * file = &info->files[i];
305        tr_benc * d = tr_bencListAddDict( list, 3 );
306        tr_bencDictAddInt( d, "bytesCompleted", files[i].bytesCompleted );
307        tr_bencDictAddInt( d, "priority", file->priority );
308        tr_bencDictAddBool( d, "wanted", !file->dnd );
309    }
310
311    tr_torrentFilesFree( files, n );
312}
313
314static void
315addFiles( const tr_torrent * tor,
316          tr_benc *          list )
317{
318    tr_file_index_t i;
319    tr_file_index_t n;
320    const tr_info * info = tr_torrentInfo( tor );
321    tr_file_stat *  files = tr_torrentFiles( tor, &n );
322
323    for( i = 0; i < info->fileCount; ++i )
324    {
325        const tr_file * file = &info->files[i];
326        tr_benc *       d = tr_bencListAddDict( list, 3 );
327        tr_bencDictAddInt( d, "bytesCompleted", files[i].bytesCompleted );
328        tr_bencDictAddInt( d, "length", file->length );
329        tr_bencDictAddStr( d, "name", file->name );
330    }
331
332    tr_torrentFilesFree( files, n );
333}
334
335static void
336addWebseeds( const tr_info * info,
337             tr_benc *       webseeds )
338{
339    int i;
340
341    for( i = 0; i < info->webseedCount; ++i )
342        tr_bencListAddStr( webseeds, info->webseeds[i] );
343}
344
345static void
346addTrackers( const tr_info * info,
347             tr_benc *       trackers )
348{
349    int i;
350
351    for( i = 0; i < info->trackerCount; ++i )
352    {
353        const tr_tracker_info * t = &info->trackers[i];
354        tr_benc *               d = tr_bencListAddDict( trackers, 3 );
355        tr_bencDictAddStr( d, "announce", t->announce );
356        tr_bencDictAddStr( d, "scrape", t->scrape );
357        tr_bencDictAddInt( d, "tier", t->tier );
358    }
359}
360
361static void
362addPeers( const tr_torrent * tor,
363          tr_benc *          list )
364{
365    int            i;
366    int            peerCount;
367    tr_peer_stat * peers = tr_torrentPeers( tor, &peerCount );
368
369    tr_bencInitList( list, peerCount );
370
371    for( i = 0; i < peerCount; ++i )
372    {
373        tr_benc *            d = tr_bencListAddDict( list, 14 );
374        const tr_peer_stat * peer = peers + i;
375        tr_bencDictAddStr ( d, "address", peer->addr );
376        tr_bencDictAddStr ( d, "clientName", peer->client );
377        tr_bencDictAddBool( d, "clientIsChoked", peer->clientIsChoked );
378        tr_bencDictAddBool( d, "clientIsInterested", peer->clientIsInterested );
379        tr_bencDictAddStr ( d, "flagStr", peer->flagStr );
380        tr_bencDictAddBool( d, "isDownloadingFrom", peer->isDownloadingFrom );
381        tr_bencDictAddBool( d, "isEncrypted", peer->isEncrypted );
382        tr_bencDictAddBool( d, "isIncoming", peer->isIncoming );
383        tr_bencDictAddBool( d, "isUploadingTo", peer->isUploadingTo );
384        tr_bencDictAddBool( d, "peerIsChoked", peer->peerIsChoked );
385        tr_bencDictAddBool( d, "peerIsInterested", peer->peerIsInterested );
386        tr_bencDictAddInt ( d, "port", peer->port );
387        tr_bencDictAddReal( d, "progress", peer->progress );
388        tr_bencDictAddInt ( d, "rateToClient", (int)( peer->rateToClient * 1024.0 ) );
389        tr_bencDictAddInt ( d, "rateToPeer", (int)( peer->rateToPeer * 1024.0 ) );
390    }
391
392    tr_torrentPeersFree( peers, peerCount );
393}
394
395static void
396addField( const tr_torrent * tor,
397          tr_benc *          d,
398          const char *       key )
399{
400    const tr_info * inf = tr_torrentInfo( tor );
401    const tr_stat * st = tr_torrentStat( (tr_torrent*)tor );
402
403    if( !strcmp( key, "activityDate" ) )
404        tr_bencDictAddInt( d, key, st->activityDate );
405    else if( !strcmp( key, "addedDate" ) )
406        tr_bencDictAddInt( d, key, st->addedDate );
407    else if( !strcmp( key, "announceResponse" ) )
408        tr_bencDictAddStr( d, key, st->announceResponse );
409    else if( !strcmp( key, "announceURL" ) )
410        tr_bencDictAddStr( d, key, st->announceURL );
411    else if( !strcmp( key, "comment" ) )
412        tr_bencDictAddStr( d, key, inf->comment ? inf->comment : "" );
413    else if( !strcmp( key, "corruptEver" ) )
414        tr_bencDictAddInt( d, key, st->corruptEver );
415    else if( !strcmp( key, "creator" ) )
416        tr_bencDictAddStr( d, key, inf->creator ? inf->creator : "" );
417    else if( !strcmp( key, "dateCreated" ) )
418        tr_bencDictAddInt( d, key, inf->dateCreated );
419    else if( !strcmp( key, "desiredAvailable" ) )
420        tr_bencDictAddInt( d, key, st->desiredAvailable );
421    else if( !strcmp( key, "doneDate" ) )
422        tr_bencDictAddInt( d, key, st->doneDate );
423    else if( !strcmp( key, "downloadDir" ) )
424        tr_bencDictAddStr( d, key, tr_torrentGetDownloadDir( tor ) );
425    else if( !strcmp( key, "downloadedEver" ) )
426        tr_bencDictAddInt( d, key, st->downloadedEver );
427    else if( !strcmp( key, "downloaders" ) )
428        tr_bencDictAddInt( d, key, st->downloaders );
429    else if( !strcmp( key, "downloadLimit" ) )
430        tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit( tor, TR_DOWN ) );
431    else if( !strcmp( key, "downloadLimited" ) )
432        tr_bencDictAddBool( d, key, tr_torrentUsesSpeedLimit( tor, TR_DOWN ) );
433    else if( !strcmp( key, "error" ) )
434        tr_bencDictAddInt( d, key, st->error );
435    else if( !strcmp( key, "errorString" ) )
436        tr_bencDictAddStr( d, key, st->errorString );
437    else if( !strcmp( key, "eta" ) )
438        tr_bencDictAddInt( d, key, st->eta );
439    else if( !strcmp( key, "files" ) )
440        addFiles( tor, tr_bencDictAddList( d, key, inf->fileCount ) );
441    else if( !strcmp( key, "fileStats" ) )
442        addFileStats( tor, tr_bencDictAddList( d, key, inf->fileCount ) );
443    else if( !strcmp( key, "hashString" ) )
444        tr_bencDictAddStr( d, key, tor->info.hashString );
445    else if( !strcmp( key, "haveUnchecked" ) )
446        tr_bencDictAddInt( d, key, st->haveUnchecked );
447    else if( !strcmp( key, "haveValid" ) )
448        tr_bencDictAddInt( d, key, st->haveValid );
449    else if( !strcmp( key, "honorsSessionLimits" ) )
450        tr_bencDictAddBool( d, key, tr_torrentUsesSessionLimits( tor ) );
451    else if( !strcmp( key, "id" ) )
452        tr_bencDictAddInt( d, key, st->id );
453    else if( !strcmp( key, "isPrivate" ) )
454        tr_bencDictAddBool( d, key, tr_torrentIsPrivate( tor ) );
455    else if( !strcmp( key, "lastAnnounceTime" ) )
456        tr_bencDictAddInt( d, key, st->lastAnnounceTime );
457    else if( !strcmp( key, "lastScrapeTime" ) )
458        tr_bencDictAddInt( d, key, st->lastScrapeTime );
459    else if( !strcmp( key, "leechers" ) )
460        tr_bencDictAddInt( d, key, st->leechers );
461    else if( !strcmp( key, "leftUntilDone" ) )
462        tr_bencDictAddInt( d, key, st->leftUntilDone );
463    else if( !strcmp( key, "manualAnnounceTime" ) )
464        tr_bencDictAddInt( d, key, st->manualAnnounceTime );
465    else if( !strcmp( key, "maxConnectedPeers" ) )
466        tr_bencDictAddInt( d, key,  tr_torrentGetPeerLimit( tor ) );
467    else if( !strcmp( key, "name" ) )
468        tr_bencDictAddStr( d, key, inf->name );
469    else if( !strcmp( key, "nextAnnounceTime" ) )
470        tr_bencDictAddInt( d, key, st->nextAnnounceTime );
471    else if( !strcmp( key, "nextScrapeTime" ) )
472        tr_bencDictAddInt( d, key, st->nextScrapeTime );
473    else if( !strcmp( key, "percentDone" ) )
474        tr_bencDictAddReal( d, key, st->percentDone );
475    else if( !strcmp( key, "peer-limit" ) )
476        tr_bencDictAddInt( d, key, tr_torrentGetPeerLimit( tor ) );
477    else if( !strcmp( key, "peers" ) )
478        addPeers( tor, tr_bencDictAdd( d, key ) );
479    else if( !strcmp( key, "peersConnected" ) )
480        tr_bencDictAddInt( d, key, st->peersConnected );
481    else if( !strcmp( key, "peersFrom" ) )
482    {
483        tr_benc *   tmp = tr_bencDictAddDict( d, key, 4 );
484        const int * f = st->peersFrom;
485        tr_bencDictAddInt( tmp, "fromCache",    f[TR_PEER_FROM_CACHE] );
486        tr_bencDictAddInt( tmp, "fromIncoming", f[TR_PEER_FROM_INCOMING] );
487        tr_bencDictAddInt( tmp, "fromPex",      f[TR_PEER_FROM_PEX] );
488        tr_bencDictAddInt( tmp, "fromTracker",  f[TR_PEER_FROM_TRACKER] );
489    }
490    else if( !strcmp( key, "peersGettingFromUs" ) )
491        tr_bencDictAddInt( d, key, st->peersGettingFromUs );
492    else if( !strcmp( key, "peersKnown" ) )
493        tr_bencDictAddInt( d, key, st->peersKnown );
494    else if( !strcmp( key, "peersSendingToUs" ) )
495        tr_bencDictAddInt( d, key, st->peersSendingToUs );
496    else if( !strcmp( key, "pieces" ) ) {
497        const tr_bitfield * pieces = tr_cpPieceBitfield( &tor->completion );
498        char * str = tr_base64_encode( pieces->bits, pieces->byteCount, NULL );
499        tr_bencDictAddStr( d, key, str );
500        tr_free( str );
501    }
502    else if( !strcmp( key, "pieceCount" ) )
503        tr_bencDictAddInt( d, key, inf->pieceCount );
504    else if( !strcmp( key, "pieceSize" ) )
505        tr_bencDictAddInt( d, key, inf->pieceSize );
506    else if( !strcmp( key, "priorities" ) )
507    {
508        tr_file_index_t i;
509        tr_benc *       p = tr_bencDictAddList( d, key, inf->fileCount );
510        for( i = 0; i < inf->fileCount; ++i )
511            tr_bencListAddInt( p, inf->files[i].priority );
512    }
513    else if( !strcmp( key, "rateDownload" ) )
514        tr_bencDictAddInt( d, key, (int)( st->pieceDownloadSpeed * 1024 ) );
515    else if( !strcmp( key, "rateUpload" ) )
516        tr_bencDictAddInt( d, key, (int)( st->pieceUploadSpeed * 1024 ) );
517    else if( !strcmp( key, "ratio" ) )
518        tr_bencDictAddReal( d, key, st->ratio );
519    else if( !strcmp( key, "recheckProgress" ) )
520        tr_bencDictAddReal( d, key, st->recheckProgress );
521    else if( !strcmp( key, "scrapeResponse" ) )
522        tr_bencDictAddStr( d, key, st->scrapeResponse );
523    else if( !strcmp( key, "scrapeURL" ) )
524        tr_bencDictAddStr( d, key, st->scrapeURL );
525    else if( !strcmp( key, "seeders" ) )
526        tr_bencDictAddInt( d, key, st->seeders );
527    else if( !strcmp( key, "seedRatioLimit" ) )
528        tr_bencDictAddReal( d, key, tr_torrentGetRatioLimit( tor ) );
529    else if( !strcmp( key, "seedRatioMode" ) )
530        tr_bencDictAddInt( d, key, tr_torrentGetRatioMode( tor ) );
531    else if( !strcmp( key, "sizeWhenDone" ) )
532        tr_bencDictAddInt( d, key, st->sizeWhenDone );
533    else if( !strcmp( key, "startDate" ) )
534        tr_bencDictAddInt( d, key, st->startDate );
535    else if( !strcmp( key, "status" ) )
536        tr_bencDictAddInt( d, key, st->activity );
537    else if( !strcmp( key, "swarmSpeed" ) )
538        tr_bencDictAddInt( d, key, (int)( st->swarmSpeed * 1024 ) );
539    else if( !strcmp( key, "timesCompleted" ) )
540        tr_bencDictAddInt( d, key, st->timesCompleted );
541    else if( !strcmp( key, "trackers" ) )
542        addTrackers( inf, tr_bencDictAddList( d, key, inf->trackerCount ) );
543    else if( !strcmp( key, "torrentFile" ) )
544        tr_bencDictAddStr( d, key, inf->torrent );
545    else if( !strcmp( key, "totalSize" ) )
546        tr_bencDictAddInt( d, key, inf->totalSize );
547    else if( !strcmp( key, "uploadedEver" ) )
548        tr_bencDictAddInt( d, key, st->uploadedEver );
549    else if( !strcmp( key, "uploadRatio" ) )
550        tr_bencDictAddReal( d, key, tr_getRatio( st->uploadedEver, st->downloadedEver ) );
551    else if( !strcmp( key, "uploadLimit" ) )
552        tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit( tor, TR_UP ) );
553    else if( !strcmp( key, "uploadLimited" ) )
554        tr_bencDictAddBool( d, key, tr_torrentUsesSpeedLimit( tor, TR_UP ) );
555    else if( !strcmp( key, "wanted" ) )
556    {
557        tr_file_index_t i;
558        tr_benc *       w = tr_bencDictAddList( d, key, inf->fileCount );
559        for( i = 0; i < inf->fileCount; ++i )
560            tr_bencListAddInt( w, inf->files[i].dnd ? 0 : 1 );
561    }
562    else if( !strcmp( key, "webseeds" ) )
563        addWebseeds( inf, tr_bencDictAddList( d, key, inf->trackerCount ) );
564    else if( !strcmp( key, "webseedsSendingToUs" ) )
565        tr_bencDictAddInt( d, key, st->webseedsSendingToUs );
566}
567
568static void
569addInfo( const tr_torrent * tor,
570         tr_benc *          d,
571         tr_benc *          fields )
572{
573    int          i;
574    const int    n = tr_bencListSize( fields );
575    const char * str;
576
577    tr_bencInitDict( d, n );
578
579    for( i = 0; i < n; ++i )
580        if( tr_bencGetStr( tr_bencListChild( fields, i ), &str ) )
581            addField( tor, d, str );
582}
583
584static const char*
585torrentGet( tr_session               * session,
586            tr_benc                  * args_in,
587            tr_benc                  * args_out,
588            struct tr_rpc_idle_data  * idle_data )
589{
590    int           i, torrentCount;
591    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
592    tr_benc *     list = tr_bencDictAddList( args_out, "torrents", torrentCount );
593    tr_benc *     fields;
594    const char *  msg = NULL;
595    const char *  strVal;
596
597    assert( idle_data == NULL );
598
599    if( tr_bencDictFindStr( args_in, "ids", &strVal ) && !strcmp( strVal, "recently-active" ) ) {
600        int n = 0;
601        tr_benc * d;
602        const time_t now = time( NULL );
603        const int interval = RECENTLY_ACTIVE_SECONDS;
604        tr_benc * removed_out = tr_bencDictAddList( args_out, "removed", 0 );
605        while(( d = tr_bencListChild( &session->removedTorrents, n++ ))) {
606            int64_t intVal;
607            if( tr_bencDictFindInt( d, "date", &intVal ) && ( intVal >= now - interval ) ) {
608                tr_bencDictFindInt( d, "id", &intVal );
609                tr_bencListAddInt( removed_out, intVal );
610            }
611        }
612    }
613
614    if( !tr_bencDictFindList( args_in, "fields", &fields ) )
615        msg = "no fields specified";
616    else for( i = 0; i < torrentCount; ++i )
617            addInfo( torrents[i], tr_bencListAdd( list ), fields );
618
619    tr_free( torrents );
620    return msg;
621}
622
623/***
624****
625***/
626
627static const char*
628setFilePriorities( tr_torrent * tor,
629                   int          priority,
630                   tr_benc *    list )
631{
632    int i;
633    int64_t tmp;
634    int fileCount = 0;
635    const int n = tr_bencListSize( list );
636    const char * errmsg = NULL;
637    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
638
639    if( n )
640    {
641        for( i = 0; i < n; ++i ) {
642            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) {
643                if( 0 <= tmp && tmp < tor->info.fileCount ) {
644                    files[fileCount++] = tmp;
645                } else {
646                    errmsg = "file index out of range";
647                }
648            }
649        }
650    }
651    else /* if empty set, apply to all */
652    {
653        tr_file_index_t t;
654        for( t = 0; t < tor->info.fileCount; ++t )
655            files[fileCount++] = t;
656    }
657
658    if( fileCount )
659        tr_torrentSetFilePriorities( tor, files, fileCount, priority );
660
661    tr_free( files );
662    return errmsg;
663}
664
665static const char*
666setFileDLs( tr_torrent * tor,
667            int          do_download,
668            tr_benc *    list )
669{
670    int i;
671    int64_t tmp;
672    int fileCount = 0;
673    const int n = tr_bencListSize( list );
674    const char * errmsg = NULL;
675    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
676
677    if( n ) /* if argument list, process them */
678    {
679        for( i = 0; i < n; ++i ) {
680            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) {
681                if( 0 <= tmp && tmp < tor->info.fileCount ) {
682                    files[fileCount++] = tmp;
683                } else {
684                    errmsg = "file index out of range";
685                }
686            }
687        }
688    }
689    else /* if empty set, apply to all */
690    {
691        tr_file_index_t t;
692        for( t = 0; t < tor->info.fileCount; ++t )
693            files[fileCount++] = t;
694    }
695
696    if( fileCount )
697        tr_torrentSetFileDLs( tor, files, fileCount, do_download );
698
699    tr_free( files );
700    return errmsg;
701}
702
703static const char*
704torrentSet( tr_session               * session,
705            tr_benc                  * args_in,
706            tr_benc                  * args_out UNUSED,
707            struct tr_rpc_idle_data  * idle_data )
708{
709    const char * errmsg = NULL;
710    int i, torrentCount;
711    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
712
713    assert( idle_data == NULL );
714
715    for( i = 0; i < torrentCount; ++i )
716    {
717        int64_t      tmp;
718        double       d;
719        tr_benc *    files;
720        tr_bool      boolVal;
721        tr_torrent * tor = torrents[i];
722
723        if( tr_bencDictFindList( args_in, "files-unwanted", &files ) )
724            setFileDLs( tor, FALSE, files );
725        if( tr_bencDictFindList( args_in, "files-wanted", &files ) )
726            setFileDLs( tor, TRUE, files );
727        if( tr_bencDictFindInt( args_in, "peer-limit", &tmp ) )
728            tr_torrentSetPeerLimit( tor, tmp );
729        if( !errmsg &&  tr_bencDictFindList( args_in, "priority-high", &files ) )
730            errmsg = setFilePriorities( tor, TR_PRI_HIGH, files );
731        if( !errmsg && tr_bencDictFindList( args_in, "priority-low", &files ) )
732            errmsg = setFilePriorities( tor, TR_PRI_LOW, files );
733        if( !errmsg && tr_bencDictFindList( args_in, "priority-normal", &files ) )
734            errmsg = setFilePriorities( tor, TR_PRI_NORMAL, files );
735        if( tr_bencDictFindInt( args_in, "downloadLimit", &tmp ) )
736            tr_torrentSetSpeedLimit( tor, TR_DOWN, tmp );
737        if( tr_bencDictFindBool( args_in, "downloadLimited", &boolVal ) )
738            tr_torrentUseSpeedLimit( tor, TR_DOWN, boolVal );
739        if( tr_bencDictFindBool( args_in, "honorsSessionLimits", &boolVal ) )
740            tr_torrentUseSessionLimits( tor, boolVal );
741        if( tr_bencDictFindInt( args_in, "uploadLimit", &tmp ) )
742            tr_torrentSetSpeedLimit( tor, TR_UP, tmp );
743        if( tr_bencDictFindBool( args_in, "uploadLimited", &boolVal ) )
744            tr_torrentUseSpeedLimit( tor, TR_UP, boolVal );
745        if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
746            tr_torrentSetRatioLimit( tor, d );
747        if( tr_bencDictFindInt( args_in, "seedRatioMode", &tmp ) )
748            tr_torrentSetRatioMode( tor, tmp );
749        notify( session, TR_RPC_TORRENT_CHANGED, tor );
750    }
751
752    tr_free( torrents );
753    return errmsg;
754}
755
756/***
757****
758***/
759
760static void
761portTested( tr_session       * session UNUSED,
762            long               response_code,
763            const void       * response,
764            size_t             response_byte_count,
765            void             * user_data )
766{
767    char result[1024];
768    struct tr_rpc_idle_data * data = user_data;
769
770    if( response_code != 200 )
771    {
772        tr_snprintf( result, sizeof( result ), "http error %ld: %s",
773                     response_code, tr_webGetResponseStr( response_code ) );
774    }
775    else /* success */
776    {
777        const tr_bool isOpen = response_byte_count && *(char*)response == '1';
778        tr_bencDictAddBool( data->args_out, "port-is-open", isOpen );
779        tr_snprintf( result, sizeof( result ), "success" );
780    }
781
782    tr_idle_function_done( data, result );
783}
784
785static const char*
786portTest( tr_session               * session,
787          tr_benc                  * args_in UNUSED,
788          tr_benc                  * args_out UNUSED,
789          struct tr_rpc_idle_data  * idle_data )
790{
791    const int port = tr_sessionGetPeerPort( session );
792    char * url = tr_strdup_printf( "http://portcheck.transmissionbt.com/%d", port );
793    tr_webRun( session, url, NULL, portTested, idle_data );
794    tr_free( url );
795    return NULL;
796}
797
798/***
799****
800***/
801
802static void
803gotNewBlocklist( tr_session       * session,
804                 long               response_code,
805                 const void       * response,
806                 size_t             response_byte_count,
807                 void             * user_data )
808{
809    char result[1024];
810    struct tr_rpc_idle_data * data = user_data;
811
812    if( response_code != 200 )
813    {
814        tr_snprintf( result, sizeof( result ), "http error %ld: %s",
815                     response_code, tr_webGetResponseStr( response_code ) );
816    }
817    else /* success */
818    {
819        int ruleCount;
820        char * filename = tr_buildPath( tr_sessionGetConfigDir( session ), "blocklist.tmp", NULL );
821        FILE * fp;
822
823        /* download a new blocklist */
824        fp = fopen( filename, "w+" );
825        fwrite( response, 1, response_byte_count, fp );
826        fclose( fp );
827
828        /* feed it to the session */
829        ruleCount = tr_blocklistSetContent( session, filename );
830
831        /* give the client a response */
832        tr_bencDictAddInt( data->args_out, "blocklist-size", ruleCount );
833        tr_snprintf( result, sizeof( result ), "success" );
834
835        /* cleanup */
836        unlink( filename );
837        tr_free( filename );
838    }
839
840    tr_idle_function_done( data, result );
841}
842
843static const char*
844blocklistUpdate( tr_session               * session,
845                 tr_benc                  * args_in UNUSED,
846                 tr_benc                  * args_out UNUSED,
847                 struct tr_rpc_idle_data  * idle_data )
848{
849    /* FIXME: use this url after the website's updated */
850    /* const char * url = "http://update.transmissionbt.com/level1"; */
851    const char * url = "http://download.m0k.org/transmission/files/level1";
852    tr_webRun( session, url, NULL, gotNewBlocklist, idle_data );
853    return NULL;
854}
855
856/***
857****
858***/
859
860static void
861addTorrentImpl( struct tr_rpc_idle_data * data, tr_ctor * ctor )
862{
863    int err = 0;
864    const char * result = NULL;
865    tr_torrent * tor = tr_torrentNew( ctor, &err );
866
867    tr_ctorFree( ctor );
868
869    if( tor )
870    {
871        tr_benc fields;
872        tr_bencInitList( &fields, 3 );
873        tr_bencListAddStr( &fields, "id" );
874        tr_bencListAddStr( &fields, "name" );
875        tr_bencListAddStr( &fields, "hashString" );
876        addInfo( tor, tr_bencDictAdd( data->args_out, "torrent-added" ), &fields );
877        notify( data->session, TR_RPC_TORRENT_ADDED, tor );
878        tr_bencFree( &fields );
879    }
880    else if( err == TR_EDUPLICATE )
881    {
882        result = "duplicate torrent";
883    }
884    else if( err == TR_EINVALID )
885    {
886        result = "invalid or corrupt torrent file";
887    }
888
889    tr_idle_function_done( data, result );
890}
891
892
893struct add_torrent_idle_data
894{
895    struct tr_rpc_idle_data * data;
896    tr_ctor * ctor;
897};
898
899static void
900gotMetadataFromURL( tr_session       * session UNUSED,
901                    long               response_code,
902                    const void       * response,
903                    size_t             response_byte_count,
904                    void             * user_data )
905{
906    struct add_torrent_idle_data * data = user_data;
907
908    dbgmsg( "torrentAdd: HTTP response code was %ld (%s); response length was %zu bytes",
909            response_code, tr_webGetResponseStr( response_code ), response_byte_count );
910
911    if( response_code == 200 )
912    {
913        tr_ctorSetMetainfo( data->ctor, response, response_byte_count );
914        addTorrentImpl( data->data, data->ctor );
915    }
916    else
917    {
918        char result[1024];
919        tr_snprintf( result, sizeof( result ), "http error %ld: %s",
920                     response_code, tr_webGetResponseStr( response_code ) );
921        tr_idle_function_done( data->data, result );
922    }
923
924    tr_free( data );
925}
926
927static tr_bool
928isCurlURL( const char * filename )
929{
930    if( filename == NULL )
931        return FALSE;
932
933    return ( strstr( filename, "ftp://" ) != NULL )
934        || ( strstr( filename, "http://" ) != NULL )
935        || ( strstr( filename, "https://" ) != NULL );
936}
937
938static tr_file_index_t*
939fileListFromList( tr_benc * list, tr_file_index_t * setmeCount )
940{
941    size_t i;
942    const size_t childCount = tr_bencListSize( list );
943    tr_file_index_t n = 0;
944    tr_file_index_t * files = tr_new0( tr_file_index_t, childCount );
945
946    for( i=0; i<childCount; ++i ) {
947        int64_t intVal;
948        if( tr_bencGetInt( tr_bencListChild( list, i ), &intVal ) )
949            files[n++] = (tr_file_index_t)intVal;
950    }
951
952    *setmeCount = n;
953    return files;
954}
955
956static const char*
957torrentAdd( tr_session               * session,
958            tr_benc                  * args_in,
959            tr_benc                  * args_out UNUSED,
960            struct tr_rpc_idle_data  * idle_data )
961{
962    const char * filename = NULL;
963    const char * metainfo_base64 = NULL;
964
965    assert( idle_data != NULL );
966
967    tr_bencDictFindStr( args_in, "filename", &filename );
968    tr_bencDictFindStr( args_in, "metainfo", &metainfo_base64 );
969    if( !filename && !metainfo_base64 )
970        return "no filename or metainfo specified";
971    else
972    {
973        int64_t      i;
974        tr_bool      boolVal;
975        const char * str;
976        tr_benc    * l;
977        tr_ctor    * ctor = tr_ctorNew( session );
978
979        /* set the optional arguments */
980
981        if( tr_bencDictFindStr( args_in, "download-dir", &str ) )
982            tr_ctorSetDownloadDir( ctor, TR_FORCE, str );
983
984        if( tr_bencDictFindBool( args_in, "paused", &boolVal ) )
985            tr_ctorSetPaused( ctor, TR_FORCE, boolVal );
986
987        if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
988            tr_ctorSetPeerLimit( ctor, TR_FORCE, i );
989
990        if( tr_bencDictFindList( args_in, "files-unwanted", &l ) ) {
991            tr_file_index_t fileCount;
992            tr_file_index_t * files = fileListFromList( l, &fileCount );
993            tr_ctorSetFilesWanted( ctor, files, fileCount, FALSE );
994            tr_free( files );
995        }
996        if( tr_bencDictFindList( args_in, "files-wanted", &l ) ) {
997            tr_file_index_t fileCount;
998            tr_file_index_t * files = fileListFromList( l, &fileCount );
999            tr_ctorSetFilesWanted( ctor, files, fileCount, TRUE );
1000            tr_free( files );
1001        }
1002
1003        if( tr_bencDictFindList( args_in, "priority-low", &l ) ) {
1004            tr_file_index_t fileCount;
1005            tr_file_index_t * files = fileListFromList( l, &fileCount );
1006            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_LOW );
1007            tr_free( files );
1008        }
1009        if( tr_bencDictFindList( args_in, "priority-normal", &l ) ) {
1010            tr_file_index_t fileCount;
1011            tr_file_index_t * files = fileListFromList( l, &fileCount );
1012            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_NORMAL );
1013            tr_free( files );
1014        }
1015        if( tr_bencDictFindList( args_in, "priority-high", &l ) ) {
1016            tr_file_index_t fileCount;
1017            tr_file_index_t * files = fileListFromList( l, &fileCount );
1018            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_HIGH );
1019            tr_free( files );
1020        }
1021
1022        dbgmsg( "torrentAdd: filename is \"%s\"", filename );
1023
1024        if( isCurlURL( filename ) )
1025        {
1026            struct add_torrent_idle_data * d = tr_new0( struct add_torrent_idle_data, 1 );
1027            d->data = idle_data;
1028            d->ctor = ctor;
1029            tr_webRun( session, filename, NULL, gotMetadataFromURL, d );
1030        }
1031        else
1032        {
1033            if( filename != NULL )
1034                tr_ctorSetMetainfoFromFile( ctor, filename );
1035            else {
1036                int len;
1037                char * metainfo = tr_base64_decode( metainfo_base64, -1,  &len );
1038                tr_ctorSetMetainfo( ctor, (uint8_t*)metainfo, len );
1039                tr_free( metainfo );
1040            }
1041            addTorrentImpl( idle_data, ctor );
1042        }
1043
1044    }
1045
1046    return NULL;
1047}
1048
1049/***
1050****
1051***/
1052
1053static const char*
1054sessionSet( tr_session               * session,
1055            tr_benc                  * args_in,
1056            tr_benc                  * args_out UNUSED,
1057            struct tr_rpc_idle_data  * idle_data )
1058{
1059    int64_t      i;
1060    double       d;
1061    tr_bool      boolVal;
1062    const char * str;
1063
1064    assert( idle_data == NULL );
1065
1066    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_UP, &i ) )
1067        tr_sessionSetAltSpeed( session, TR_UP, i );
1068    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_DOWN, &i ) )
1069        tr_sessionSetAltSpeed( session, TR_DOWN, i );
1070    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_ENABLED, &boolVal ) )
1071        tr_sessionUseAltSpeed( session, boolVal );
1072    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &i ) )
1073        tr_sessionSetAltSpeedBegin( session, i );
1074    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_END, &i ) )
1075        tr_sessionSetAltSpeedEnd( session, i );
1076    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &i ) )
1077        tr_sessionSetAltSpeedDay( session, i );
1078    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &boolVal ) )
1079        tr_sessionUseAltSpeedTime( session, boolVal );
1080    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_BLOCKLIST_ENABLED, &boolVal ) )
1081        tr_blocklistSetEnabled( session, boolVal );
1082    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1083        tr_sessionSetDownloadDir( session, str );
1084    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &i ) )
1085        tr_sessionSetPeerLimit( session, i );
1086    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i ) )
1087        tr_sessionSetPeerLimitPerTorrent( session, i );
1088    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) )
1089        tr_sessionSetPexEnabled( session, boolVal );
1090    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, &boolVal ) )
1091        tr_sessionSetPeerPortRandomOnStart( session, boolVal );
1092    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_PORT, &i ) )
1093        tr_sessionSetPeerPort( session, i );
1094    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) )
1095        tr_sessionSetPortForwardingEnabled( session, boolVal );
1096    if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
1097        tr_sessionSetRatioLimit( session, d );
1098    if( tr_bencDictFindBool( args_in, "seedRatioLimited", &boolVal ) )
1099        tr_sessionSetRatioLimited( session, boolVal );
1100    if( tr_bencDictFindInt( args_in, "speed-limit-down", &i ) )
1101        tr_sessionSetSpeedLimit( session, TR_DOWN, i );
1102    if( tr_bencDictFindBool( args_in, "speed-limit-down-enabled", &boolVal ) )
1103        tr_sessionLimitSpeed( session, TR_DOWN, boolVal );
1104    if( tr_bencDictFindInt( args_in, "speed-limit-up", &i ) )
1105        tr_sessionSetSpeedLimit( session, TR_UP, i );
1106    if( tr_bencDictFindBool( args_in, "speed-limit-up-enabled", &boolVal ) )
1107        tr_sessionLimitSpeed( session, TR_UP, boolVal );
1108    if( tr_bencDictFindStr( args_in, "encryption", &str ) ) {
1109        if( !strcmp( str, "required" ) )
1110            tr_sessionSetEncryption( session, TR_ENCRYPTION_REQUIRED );
1111        else if( !strcmp( str, "tolerated" ) )
1112            tr_sessionSetEncryption( session, TR_CLEAR_PREFERRED );
1113        else
1114            tr_sessionSetEncryption( session, TR_ENCRYPTION_PREFERRED );
1115    }
1116
1117    notify( session, TR_RPC_SESSION_CHANGED, NULL );
1118
1119    return NULL;
1120}
1121
1122static const char*
1123sessionStats( tr_session               * session,
1124              tr_benc                  * args_in UNUSED,
1125              tr_benc                  * args_out,
1126              struct tr_rpc_idle_data  * idle_data )
1127{
1128    int running = 0;
1129    int total = 0;
1130    tr_benc * d; 
1131    tr_session_stats currentStats = { 0.0f, 0, 0, 0, 0, 0 }; 
1132    tr_session_stats cumulativeStats = { 0.0f, 0, 0, 0, 0, 0 }; 
1133    tr_torrent * tor = NULL;
1134
1135    assert( idle_data == NULL );
1136
1137    while(( tor = tr_torrentNext( session, tor ))) {
1138        ++total;
1139        if( tor->isRunning )
1140            ++running;
1141    }
1142
1143    tr_sessionGetStats( session, &currentStats ); 
1144    tr_sessionGetCumulativeStats( session, &cumulativeStats ); 
1145
1146    tr_bencDictAddInt( args_out, "activeTorrentCount", running );
1147    tr_bencDictAddInt( args_out, "downloadSpeed", (int)( tr_sessionGetPieceSpeed( session, TR_DOWN ) * 1024 ) );
1148    tr_bencDictAddInt( args_out, "pausedTorrentCount", total - running );
1149    tr_bencDictAddInt( args_out, "torrentCount", total );
1150    tr_bencDictAddInt( args_out, "uploadSpeed", (int)( tr_sessionGetPieceSpeed( session, TR_UP ) * 1024 ) );
1151
1152    d = tr_bencDictAddDict( args_out, "cumulative-stats", 5 ); 
1153    tr_bencDictAddInt( d, "downloadedBytes", cumulativeStats.downloadedBytes ); 
1154    tr_bencDictAddInt( d, "filesAdded", cumulativeStats.filesAdded ); 
1155    tr_bencDictAddInt( d, "secondsActive", cumulativeStats.secondsActive ); 
1156    tr_bencDictAddInt( d, "sessionCount", cumulativeStats.sessionCount ); 
1157    tr_bencDictAddInt( d, "uploadedBytes", cumulativeStats.uploadedBytes ); 
1158
1159    d = tr_bencDictAddDict( args_out, "current-stats", 5 ); 
1160    tr_bencDictAddInt( d, "downloadedBytes", currentStats.downloadedBytes ); 
1161    tr_bencDictAddInt( d, "filesAdded", currentStats.filesAdded ); 
1162    tr_bencDictAddInt( d, "secondsActive", currentStats.secondsActive ); 
1163    tr_bencDictAddInt( d, "sessionCount", currentStats.sessionCount ); 
1164    tr_bencDictAddInt( d, "uploadedBytes", currentStats.uploadedBytes ); 
1165
1166    return NULL;
1167}
1168
1169static const char*
1170sessionGet( tr_session               * s,
1171            tr_benc                  * args_in UNUSED,
1172            tr_benc                  * args_out,
1173            struct tr_rpc_idle_data  * idle_data )
1174{
1175    const char * str;
1176    tr_benc *    d = args_out;
1177
1178    assert( idle_data == NULL );
1179    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP, tr_sessionGetAltSpeed(s,TR_UP) );
1180    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN, tr_sessionGetAltSpeed(s,TR_DOWN) );
1181    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED, tr_sessionUsesAltSpeed(s) );
1182    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, tr_sessionGetAltSpeedBegin(s) );
1183    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,tr_sessionGetAltSpeedEnd(s) );
1184    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,tr_sessionGetAltSpeedDay(s) );
1185    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, tr_sessionUsesAltSpeedTime(s) );
1186    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, tr_blocklistIsEnabled( s ) );
1187    tr_bencDictAddInt ( d, "blocklist-size", tr_blocklistGetRuleCount( s ) );
1188    tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR, tr_sessionGetDownloadDir( s ) );
1189    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, tr_sessionGetPeerLimit( s ) );
1190    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT, tr_sessionGetPeerLimitPerTorrent( s ) );
1191    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED, tr_sessionIsPexEnabled( s ) );
1192    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT, tr_sessionGetPeerPort( s ) );
1193    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, tr_sessionGetPeerPortRandomOnStart( s ) );
1194    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) );
1195    tr_bencDictAddInt ( d, "rpc-version", 4 );
1196    tr_bencDictAddInt ( d, "rpc-version-minimum", 1 );
1197    tr_bencDictAddReal( d, "seedRatioLimit", tr_sessionGetRatioLimit( s ) );
1198    tr_bencDictAddBool( d, "seedRatioLimited", tr_sessionIsRatioLimited( s ) );
1199    tr_bencDictAddInt ( d, "speed-limit-up", tr_sessionGetSpeedLimit( s, TR_UP ) );
1200    tr_bencDictAddBool( d, "speed-limit-up-enabled", tr_sessionIsSpeedLimited( s, TR_UP ) );
1201    tr_bencDictAddInt ( d, "speed-limit-down", tr_sessionGetSpeedLimit( s, TR_DOWN ) );
1202    tr_bencDictAddBool( d, "speed-limit-down-enabled", tr_sessionIsSpeedLimited( s, TR_DOWN ) );
1203    tr_bencDictAddStr ( d, "version", LONG_VERSION_STRING );
1204    switch( tr_sessionGetEncryption( s ) ) {
1205        case TR_CLEAR_PREFERRED: str = "tolerated"; break;
1206        case TR_ENCRYPTION_REQUIRED: str = "required"; break; 
1207        default: str = "preferred"; break;
1208    }
1209    tr_bencDictAddStr( d, "encryption", str );
1210
1211    return NULL;
1212}
1213
1214/***
1215****
1216***/
1217
1218typedef const char* ( *handler )( tr_session*, tr_benc*, tr_benc*, struct tr_rpc_idle_data * );
1219
1220static struct method
1221{
1222    const char *  name;
1223    tr_bool       immediate;
1224    handler       func;
1225}
1226methods[] =
1227{
1228    { "port-test",          FALSE, portTest            },
1229    { "blocklist-update",   FALSE, blocklistUpdate     },
1230    { "session-get",        TRUE,  sessionGet          },
1231    { "session-set",        TRUE,  sessionSet          },
1232    { "session-stats",      TRUE,  sessionStats        },
1233    { "torrent-add",        FALSE, torrentAdd          },
1234    { "torrent-get",        TRUE,  torrentGet          },
1235    { "torrent-remove",     TRUE,  torrentRemove       },
1236    { "torrent-set",        TRUE,  torrentSet          },
1237    { "torrent-start",      TRUE,  torrentStart        },
1238    { "torrent-stop",       TRUE,  torrentStop         },
1239    { "torrent-verify",     TRUE,  torrentVerify       },
1240    { "torrent-reannounce", TRUE,  torrentReannounce   }
1241};
1242
1243static void
1244noop_response_callback( tr_session * session UNUSED,
1245                        const char * response UNUSED,
1246                        size_t       response_len UNUSED,
1247                        void       * user_data UNUSED )
1248{
1249}
1250
1251static void
1252request_exec( tr_session             * session,
1253              tr_benc                * request,
1254              tr_rpc_response_func     callback,
1255              void                   * callback_user_data )
1256{
1257    int i;
1258    const char * str;
1259    tr_benc * args_in = tr_bencDictFind( request, "arguments" );
1260    const char * result = NULL;
1261
1262    if( callback == NULL )
1263        callback = noop_response_callback;
1264
1265    /* parse the request */
1266    if( !tr_bencDictFindStr( request, "method", &str ) )
1267        result = "no method name";
1268    else {
1269        const int n = TR_N_ELEMENTS( methods );
1270        for( i = 0; i < n; ++i )
1271            if( !strcmp( str, methods[i].name ) )
1272                break;
1273        if( i ==n )
1274            result = "method name not recognized";
1275    }
1276
1277    /* if we couldn't figure out which method to use, return an error */
1278    if( result != NULL )
1279    {
1280        int64_t tag;
1281        tr_benc response;
1282        struct evbuffer * buf = tr_getBuffer( );
1283
1284        tr_bencInitDict( &response, 3 );
1285        tr_bencDictAddDict( &response, "arguments", 0 );
1286        tr_bencDictAddStr( &response, "result", result );
1287        if( tr_bencDictFindInt( request, "tag", &tag ) )
1288            tr_bencDictAddInt( &response, "tag", tag );
1289        tr_bencSaveAsJSON( &response, buf );
1290        (*callback)( session, (const char*)EVBUFFER_DATA(buf),
1291                     EVBUFFER_LENGTH( buf ), callback_user_data );
1292
1293        tr_releaseBuffer( buf );
1294        tr_bencFree( &response );
1295    }
1296    else if( methods[i].immediate )
1297    {
1298        int64_t tag;
1299        tr_benc response;
1300        tr_benc * args_out;
1301        struct evbuffer * buf = tr_getBuffer( );
1302
1303        tr_bencInitDict( &response, 3 );
1304        args_out = tr_bencDictAddDict( &response, "arguments", 0 );
1305        result = (*methods[i].func)( session, args_in, args_out, NULL );
1306        if( result == NULL )
1307            result = "success";
1308        tr_bencDictAddStr( &response, "result", result );
1309        if( tr_bencDictFindInt( request, "tag", &tag ) )
1310            tr_bencDictAddInt( &response, "tag", tag );
1311        tr_bencSaveAsJSON( &response, buf );
1312        (*callback)( session, (const char*)EVBUFFER_DATA(buf),
1313                     EVBUFFER_LENGTH(buf), callback_user_data );
1314
1315        tr_releaseBuffer( buf );
1316        tr_bencFree( &response );
1317    }
1318    else
1319    {
1320        int64_t tag;
1321        struct tr_rpc_idle_data * data = tr_new0( struct tr_rpc_idle_data, 1 );
1322        data->session = session;
1323        data->response = tr_new0( tr_benc, 1 );
1324        tr_bencInitDict( data->response, 3 );
1325        if( tr_bencDictFindInt( request, "tag", &tag ) )
1326            tr_bencDictAddInt( data->response, "tag", tag );
1327        data->args_out = tr_bencDictAddDict( data->response, "arguments", 0 );
1328        data->callback = callback;
1329        data->callback_user_data = callback_user_data;
1330        (*methods[i].func)( session, args_in, data->args_out, data );
1331    }
1332}
1333
1334void
1335tr_rpc_request_exec_json( tr_session            * session,
1336                          const void            * request_json,
1337                          int                     request_len,
1338                          tr_rpc_response_func    callback,
1339                          void                  * callback_user_data )
1340{
1341    tr_benc top;
1342    int have_content;
1343
1344    if( request_len < 0 )
1345        request_len = strlen( request_json );
1346
1347    have_content = !tr_jsonParse( request_json, request_len, &top, NULL );
1348    request_exec( session, have_content ? &top : NULL, callback, callback_user_data );
1349
1350    if( have_content )
1351        tr_bencFree( &top );
1352}
1353
1354/**
1355 * Munge the URI into a usable form.
1356 *
1357 * We have very loose typing on this to make the URIs as simple as possible:
1358 * - anything not a 'tag' or 'method' is automatically in 'arguments'
1359 * - values that are all-digits are numbers
1360 * - values that are all-digits or commas are number lists
1361 * - all other values are strings
1362 */
1363void
1364tr_rpc_parse_list_str( tr_benc     * setme,
1365                       const char  * str,
1366                       int           len )
1367
1368{
1369    int valueCount;
1370    int * values = tr_parseNumberRange( str, len, &valueCount );
1371
1372    if( valueCount == 0 )
1373        tr_bencInitStr( setme, str, len );
1374    else if( valueCount == 1 )
1375        tr_bencInitInt( setme, values[0] );
1376    else {
1377        int i;
1378        tr_bencInitList( setme, valueCount );
1379        for( i=0; i<valueCount; ++i )
1380            tr_bencListAddInt( setme, values[i] );
1381    }
1382
1383    tr_free( values );
1384}
1385
1386void
1387tr_rpc_request_exec_uri( tr_session           * session,
1388                         const void           * request_uri,
1389                         int                    request_len,
1390                         tr_rpc_response_func   callback,
1391                         void                 * callback_user_data )
1392{
1393    tr_benc      top, * args;
1394    char *       request = tr_strndup( request_uri, request_len );
1395    const char * pch;
1396
1397    tr_bencInitDict( &top, 3 );
1398    args = tr_bencDictAddDict( &top, "arguments", 0 );
1399
1400    pch = strchr( request, '?' );
1401    if( !pch ) pch = request;
1402    while( pch )
1403    {
1404        const char * delim = strchr( pch, '=' );
1405        const char * next = strchr( pch, '&' );
1406        if( delim )
1407        {
1408            char *    key = tr_strndup( pch, delim - pch );
1409            int       isArg = strcmp( key, "method" ) && strcmp( key, "tag" );
1410            tr_benc * parent = isArg ? args : &top;
1411            tr_rpc_parse_list_str( tr_bencDictAdd( parent, key ),
1412                                  delim + 1,
1413                                  next ? (size_t)(
1414                                       next -
1415                                      ( delim + 1 ) ) : strlen( delim + 1 ) );
1416            tr_free( key );
1417        }
1418        pch = next ? next + 1 : NULL;
1419    }
1420
1421    request_exec( session, &top, callback, callback_user_data );
1422
1423    /* cleanup */
1424    tr_bencFree( &top );
1425    tr_free( request );
1426}
Note: See TracBrowser for help on using the repository browser.