source: trunk/libtransmission/rpcimpl.c @ 8130

Last change on this file since 8130 was 8130, checked in by livings124, 13 years ago

libT support for #1679 Schedule Speed Limit mode based on days, not just time

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