source: trunk/libtransmission/rpcimpl.c @ 8233

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

(trunk) make it possible to #include "version.h" without having to add -I${TOP}/libtransmission/ to your CFLAGS

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