source: trunk/libtransmission/rpcimpl.c @ 8254

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

(trunk) experimental support for tr_torrentSetPriority()

  • Property svn:keywords set to Date Rev Author Id
File size: 49.8 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 8254 2009-04-18 23:17:30Z 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, "bandwidthPriority" ) )
413        tr_bencDictAddInt( d, key, tr_torrentGetPriority( tor ) );
414    else if( !strcmp( key, "comment" ) )
415        tr_bencDictAddStr( d, key, inf->comment ? inf->comment : "" );
416    else if( !strcmp( key, "corruptEver" ) )
417        tr_bencDictAddInt( d, key, st->corruptEver );
418    else if( !strcmp( key, "creator" ) )
419        tr_bencDictAddStr( d, key, inf->creator ? inf->creator : "" );
420    else if( !strcmp( key, "dateCreated" ) )
421        tr_bencDictAddInt( d, key, inf->dateCreated );
422    else if( !strcmp( key, "desiredAvailable" ) )
423        tr_bencDictAddInt( d, key, st->desiredAvailable );
424    else if( !strcmp( key, "doneDate" ) )
425        tr_bencDictAddInt( d, key, st->doneDate );
426    else if( !strcmp( key, "downloadDir" ) )
427        tr_bencDictAddStr( d, key, tr_torrentGetDownloadDir( tor ) );
428    else if( !strcmp( key, "downloadedEver" ) )
429        tr_bencDictAddInt( d, key, st->downloadedEver );
430    else if( !strcmp( key, "downloaders" ) )
431        tr_bencDictAddInt( d, key, st->downloaders );
432    else if( !strcmp( key, "downloadLimit" ) )
433        tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit( tor, TR_DOWN ) );
434    else if( !strcmp( key, "downloadLimited" ) )
435        tr_bencDictAddBool( d, key, tr_torrentUsesSpeedLimit( tor, TR_DOWN ) );
436    else if( !strcmp( key, "error" ) )
437        tr_bencDictAddInt( d, key, st->error );
438    else if( !strcmp( key, "errorString" ) )
439        tr_bencDictAddStr( d, key, st->errorString );
440    else if( !strcmp( key, "eta" ) )
441        tr_bencDictAddInt( d, key, st->eta );
442    else if( !strcmp( key, "files" ) )
443        addFiles( tor, tr_bencDictAddList( d, key, inf->fileCount ) );
444    else if( !strcmp( key, "fileStats" ) )
445        addFileStats( tor, tr_bencDictAddList( d, key, inf->fileCount ) );
446    else if( !strcmp( key, "hashString" ) )
447        tr_bencDictAddStr( d, key, tor->info.hashString );
448    else if( !strcmp( key, "haveUnchecked" ) )
449        tr_bencDictAddInt( d, key, st->haveUnchecked );
450    else if( !strcmp( key, "haveValid" ) )
451        tr_bencDictAddInt( d, key, st->haveValid );
452    else if( !strcmp( key, "honorsSessionLimits" ) )
453        tr_bencDictAddBool( d, key, tr_torrentUsesSessionLimits( tor ) );
454    else if( !strcmp( key, "id" ) )
455        tr_bencDictAddInt( d, key, st->id );
456    else if( !strcmp( key, "isPrivate" ) )
457        tr_bencDictAddBool( d, key, tr_torrentIsPrivate( tor ) );
458    else if( !strcmp( key, "lastAnnounceTime" ) )
459        tr_bencDictAddInt( d, key, st->lastAnnounceTime );
460    else if( !strcmp( key, "lastScrapeTime" ) )
461        tr_bencDictAddInt( d, key, st->lastScrapeTime );
462    else if( !strcmp( key, "leechers" ) )
463        tr_bencDictAddInt( d, key, st->leechers );
464    else if( !strcmp( key, "leftUntilDone" ) )
465        tr_bencDictAddInt( d, key, st->leftUntilDone );
466    else if( !strcmp( key, "manualAnnounceTime" ) )
467        tr_bencDictAddInt( d, key, st->manualAnnounceTime );
468    else if( !strcmp( key, "maxConnectedPeers" ) )
469        tr_bencDictAddInt( d, key,  tr_torrentGetPeerLimit( tor ) );
470    else if( !strcmp( key, "name" ) )
471        tr_bencDictAddStr( d, key, inf->name );
472    else if( !strcmp( key, "nextAnnounceTime" ) )
473        tr_bencDictAddInt( d, key, st->nextAnnounceTime );
474    else if( !strcmp( key, "nextScrapeTime" ) )
475        tr_bencDictAddInt( d, key, st->nextScrapeTime );
476    else if( !strcmp( key, "percentDone" ) )
477        tr_bencDictAddReal( d, key, st->percentDone );
478    else if( !strcmp( key, "peer-limit" ) )
479        tr_bencDictAddInt( d, key, tr_torrentGetPeerLimit( tor ) );
480    else if( !strcmp( key, "peers" ) )
481        addPeers( tor, tr_bencDictAdd( d, key ) );
482    else if( !strcmp( key, "peersConnected" ) )
483        tr_bencDictAddInt( d, key, st->peersConnected );
484    else if( !strcmp( key, "peersFrom" ) )
485    {
486        tr_benc *   tmp = tr_bencDictAddDict( d, key, 4 );
487        const int * f = st->peersFrom;
488        tr_bencDictAddInt( tmp, "fromCache",    f[TR_PEER_FROM_CACHE] );
489        tr_bencDictAddInt( tmp, "fromIncoming", f[TR_PEER_FROM_INCOMING] );
490        tr_bencDictAddInt( tmp, "fromPex",      f[TR_PEER_FROM_PEX] );
491        tr_bencDictAddInt( tmp, "fromTracker",  f[TR_PEER_FROM_TRACKER] );
492    }
493    else if( !strcmp( key, "peersGettingFromUs" ) )
494        tr_bencDictAddInt( d, key, st->peersGettingFromUs );
495    else if( !strcmp( key, "peersKnown" ) )
496        tr_bencDictAddInt( d, key, st->peersKnown );
497    else if( !strcmp( key, "peersSendingToUs" ) )
498        tr_bencDictAddInt( d, key, st->peersSendingToUs );
499    else if( !strcmp( key, "pieces" ) ) {
500        const tr_bitfield * pieces = tr_cpPieceBitfield( &tor->completion );
501        char * str = tr_base64_encode( pieces->bits, pieces->byteCount, NULL );
502        tr_bencDictAddStr( d, key, str );
503        tr_free( str );
504    }
505    else if( !strcmp( key, "pieceCount" ) )
506        tr_bencDictAddInt( d, key, inf->pieceCount );
507    else if( !strcmp( key, "pieceSize" ) )
508        tr_bencDictAddInt( d, key, inf->pieceSize );
509    else if( !strcmp( key, "priorities" ) )
510    {
511        tr_file_index_t i;
512        tr_benc *       p = tr_bencDictAddList( d, key, inf->fileCount );
513        for( i = 0; i < inf->fileCount; ++i )
514            tr_bencListAddInt( p, inf->files[i].priority );
515    }
516    else if( !strcmp( key, "rateDownload" ) )
517        tr_bencDictAddInt( d, key, (int)( st->pieceDownloadSpeed * 1024 ) );
518    else if( !strcmp( key, "rateUpload" ) )
519        tr_bencDictAddInt( d, key, (int)( st->pieceUploadSpeed * 1024 ) );
520    else if( !strcmp( key, "ratio" ) )
521        tr_bencDictAddReal( d, key, st->ratio );
522    else if( !strcmp( key, "recheckProgress" ) )
523        tr_bencDictAddReal( d, key, st->recheckProgress );
524    else if( !strcmp( key, "scrapeResponse" ) )
525        tr_bencDictAddStr( d, key, st->scrapeResponse );
526    else if( !strcmp( key, "scrapeURL" ) )
527        tr_bencDictAddStr( d, key, st->scrapeURL );
528    else if( !strcmp( key, "seeders" ) )
529        tr_bencDictAddInt( d, key, st->seeders );
530    else if( !strcmp( key, "seedRatioLimit" ) )
531        tr_bencDictAddReal( d, key, tr_torrentGetRatioLimit( tor ) );
532    else if( !strcmp( key, "seedRatioMode" ) )
533        tr_bencDictAddInt( d, key, tr_torrentGetRatioMode( tor ) );
534    else if( !strcmp( key, "sizeWhenDone" ) )
535        tr_bencDictAddInt( d, key, st->sizeWhenDone );
536    else if( !strcmp( key, "startDate" ) )
537        tr_bencDictAddInt( d, key, st->startDate );
538    else if( !strcmp( key, "status" ) )
539        tr_bencDictAddInt( d, key, st->activity );
540    else if( !strcmp( key, "swarmSpeed" ) )
541        tr_bencDictAddInt( d, key, (int)( st->swarmSpeed * 1024 ) );
542    else if( !strcmp( key, "timesCompleted" ) )
543        tr_bencDictAddInt( d, key, st->timesCompleted );
544    else if( !strcmp( key, "trackers" ) )
545        addTrackers( inf, tr_bencDictAddList( d, key, inf->trackerCount ) );
546    else if( !strcmp( key, "torrentFile" ) )
547        tr_bencDictAddStr( d, key, inf->torrent );
548    else if( !strcmp( key, "totalSize" ) )
549        tr_bencDictAddInt( d, key, inf->totalSize );
550    else if( !strcmp( key, "uploadedEver" ) )
551        tr_bencDictAddInt( d, key, st->uploadedEver );
552    else if( !strcmp( key, "uploadRatio" ) )
553        tr_bencDictAddReal( d, key, tr_getRatio( st->uploadedEver, st->downloadedEver ) );
554    else if( !strcmp( key, "uploadLimit" ) )
555        tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit( tor, TR_UP ) );
556    else if( !strcmp( key, "uploadLimited" ) )
557        tr_bencDictAddBool( d, key, tr_torrentUsesSpeedLimit( tor, TR_UP ) );
558    else if( !strcmp( key, "wanted" ) )
559    {
560        tr_file_index_t i;
561        tr_benc *       w = tr_bencDictAddList( d, key, inf->fileCount );
562        for( i = 0; i < inf->fileCount; ++i )
563            tr_bencListAddInt( w, inf->files[i].dnd ? 0 : 1 );
564    }
565    else if( !strcmp( key, "webseeds" ) )
566        addWebseeds( inf, tr_bencDictAddList( d, key, inf->trackerCount ) );
567    else if( !strcmp( key, "webseedsSendingToUs" ) )
568        tr_bencDictAddInt( d, key, st->webseedsSendingToUs );
569}
570
571static void
572addInfo( const tr_torrent * tor,
573         tr_benc *          d,
574         tr_benc *          fields )
575{
576    int          i;
577    const int    n = tr_bencListSize( fields );
578    const char * str;
579
580    tr_bencInitDict( d, n );
581
582    for( i = 0; i < n; ++i )
583        if( tr_bencGetStr( tr_bencListChild( fields, i ), &str ) )
584            addField( tor, d, str );
585}
586
587static const char*
588torrentGet( tr_session               * session,
589            tr_benc                  * args_in,
590            tr_benc                  * args_out,
591            struct tr_rpc_idle_data  * idle_data )
592{
593    int           i, torrentCount;
594    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
595    tr_benc *     list = tr_bencDictAddList( args_out, "torrents", torrentCount );
596    tr_benc *     fields;
597    const char *  msg = NULL;
598    const char *  strVal;
599
600    assert( idle_data == NULL );
601
602    if( tr_bencDictFindStr( args_in, "ids", &strVal ) && !strcmp( strVal, "recently-active" ) ) {
603        int n = 0;
604        tr_benc * d;
605        const time_t now = time( NULL );
606        const int interval = RECENTLY_ACTIVE_SECONDS;
607        tr_benc * removed_out = tr_bencDictAddList( args_out, "removed", 0 );
608        while(( d = tr_bencListChild( &session->removedTorrents, n++ ))) {
609            int64_t intVal;
610            if( tr_bencDictFindInt( d, "date", &intVal ) && ( intVal >= now - interval ) ) {
611                tr_bencDictFindInt( d, "id", &intVal );
612                tr_bencListAddInt( removed_out, intVal );
613            }
614        }
615    }
616
617    if( !tr_bencDictFindList( args_in, "fields", &fields ) )
618        msg = "no fields specified";
619    else for( i = 0; i < torrentCount; ++i )
620            addInfo( torrents[i], tr_bencListAdd( list ), fields );
621
622    tr_free( torrents );
623    return msg;
624}
625
626/***
627****
628***/
629
630static const char*
631setFilePriorities( tr_torrent * tor,
632                   int          priority,
633                   tr_benc *    list )
634{
635    int i;
636    int64_t tmp;
637    int fileCount = 0;
638    const int n = tr_bencListSize( list );
639    const char * errmsg = NULL;
640    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
641
642    if( n )
643    {
644        for( i = 0; i < n; ++i ) {
645            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) {
646                if( 0 <= tmp && tmp < tor->info.fileCount ) {
647                    files[fileCount++] = tmp;
648                } else {
649                    errmsg = "file index out of range";
650                }
651            }
652        }
653    }
654    else /* if empty set, apply to all */
655    {
656        tr_file_index_t t;
657        for( t = 0; t < tor->info.fileCount; ++t )
658            files[fileCount++] = t;
659    }
660
661    if( fileCount )
662        tr_torrentSetFilePriorities( tor, files, fileCount, priority );
663
664    tr_free( files );
665    return errmsg;
666}
667
668static const char*
669setFileDLs( tr_torrent * tor,
670            int          do_download,
671            tr_benc *    list )
672{
673    int i;
674    int64_t tmp;
675    int fileCount = 0;
676    const int n = tr_bencListSize( list );
677    const char * errmsg = NULL;
678    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
679
680    if( n ) /* if argument list, process them */
681    {
682        for( i = 0; i < n; ++i ) {
683            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) {
684                if( 0 <= tmp && tmp < tor->info.fileCount ) {
685                    files[fileCount++] = tmp;
686                } else {
687                    errmsg = "file index out of range";
688                }
689            }
690        }
691    }
692    else /* if empty set, apply to all */
693    {
694        tr_file_index_t t;
695        for( t = 0; t < tor->info.fileCount; ++t )
696            files[fileCount++] = t;
697    }
698
699    if( fileCount )
700        tr_torrentSetFileDLs( tor, files, fileCount, do_download );
701
702    tr_free( files );
703    return errmsg;
704}
705
706static const char*
707torrentSet( tr_session               * session,
708            tr_benc                  * args_in,
709            tr_benc                  * args_out UNUSED,
710            struct tr_rpc_idle_data  * idle_data )
711{
712    const char * errmsg = NULL;
713    int i, torrentCount;
714    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
715
716    assert( idle_data == NULL );
717
718    for( i = 0; i < torrentCount; ++i )
719    {
720        int64_t      tmp;
721        double       d;
722        tr_benc *    files;
723        tr_bool      boolVal;
724        tr_torrent * tor = torrents[i];
725
726        if( tr_bencDictFindInt( args_in, "bandwidthPriority", &tmp ) )
727            if( tr_isPriority( tmp ) )
728                tr_torrentSetPriority( tor, tmp );
729        if( tr_bencDictFindList( args_in, "files-unwanted", &files ) )
730            setFileDLs( tor, FALSE, files );
731        if( tr_bencDictFindList( args_in, "files-wanted", &files ) )
732            setFileDLs( tor, TRUE, files );
733        if( tr_bencDictFindInt( args_in, "peer-limit", &tmp ) )
734            tr_torrentSetPeerLimit( tor, tmp );
735        if( !errmsg &&  tr_bencDictFindList( args_in, "priority-high", &files ) )
736            errmsg = setFilePriorities( tor, TR_PRI_HIGH, files );
737        if( !errmsg && tr_bencDictFindList( args_in, "priority-low", &files ) )
738            errmsg = setFilePriorities( tor, TR_PRI_LOW, files );
739        if( !errmsg && tr_bencDictFindList( args_in, "priority-normal", &files ) )
740            errmsg = setFilePriorities( tor, TR_PRI_NORMAL, files );
741        if( tr_bencDictFindInt( args_in, "downloadLimit", &tmp ) )
742            tr_torrentSetSpeedLimit( tor, TR_DOWN, tmp );
743        if( tr_bencDictFindBool( args_in, "downloadLimited", &boolVal ) )
744            tr_torrentUseSpeedLimit( tor, TR_DOWN, boolVal );
745        if( tr_bencDictFindBool( args_in, "honorsSessionLimits", &boolVal ) )
746            tr_torrentUseSessionLimits( tor, boolVal );
747        if( tr_bencDictFindInt( args_in, "uploadLimit", &tmp ) )
748            tr_torrentSetSpeedLimit( tor, TR_UP, tmp );
749        if( tr_bencDictFindBool( args_in, "uploadLimited", &boolVal ) )
750            tr_torrentUseSpeedLimit( tor, TR_UP, boolVal );
751        if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
752            tr_torrentSetRatioLimit( tor, d );
753        if( tr_bencDictFindInt( args_in, "seedRatioMode", &tmp ) )
754            tr_torrentSetRatioMode( tor, tmp );
755        notify( session, TR_RPC_TORRENT_CHANGED, tor );
756    }
757
758    tr_free( torrents );
759    return errmsg;
760}
761
762/***
763****
764***/
765
766static void
767portTested( tr_session       * session UNUSED,
768            long               response_code,
769            const void       * response,
770            size_t             response_byte_count,
771            void             * user_data )
772{
773    char result[1024];
774    struct tr_rpc_idle_data * data = user_data;
775
776    if( response_code != 200 )
777    {
778        tr_snprintf( result, sizeof( result ), "http error %ld: %s",
779                     response_code, tr_webGetResponseStr( response_code ) );
780    }
781    else /* success */
782    {
783        const tr_bool isOpen = response_byte_count && *(char*)response == '1';
784        tr_bencDictAddBool( data->args_out, "port-is-open", isOpen );
785        tr_snprintf( result, sizeof( result ), "success" );
786    }
787
788    tr_idle_function_done( data, result );
789}
790
791static const char*
792portTest( tr_session               * session,
793          tr_benc                  * args_in UNUSED,
794          tr_benc                  * args_out UNUSED,
795          struct tr_rpc_idle_data  * idle_data )
796{
797    const int port = tr_sessionGetPeerPort( session );
798    char * url = tr_strdup_printf( "http://portcheck.transmissionbt.com/%d", port );
799    tr_webRun( session, url, NULL, portTested, idle_data );
800    tr_free( url );
801    return NULL;
802}
803
804/***
805****
806***/
807
808static void
809gotNewBlocklist( tr_session       * session,
810                 long               response_code,
811                 const void       * response,
812                 size_t             response_byte_count,
813                 void             * user_data )
814{
815    char result[1024];
816    struct tr_rpc_idle_data * data = user_data;
817
818    if( response_code != 200 )
819    {
820        tr_snprintf( result, sizeof( result ), "http error %ld: %s",
821                     response_code, tr_webGetResponseStr( response_code ) );
822    }
823    else /* success */
824    {
825        int ruleCount;
826        char * filename = tr_buildPath( tr_sessionGetConfigDir( session ), "blocklist.tmp", NULL );
827        FILE * fp;
828
829        /* download a new blocklist */
830        fp = fopen( filename, "w+" );
831        fwrite( response, 1, response_byte_count, fp );
832        fclose( fp );
833
834        /* feed it to the session */
835        ruleCount = tr_blocklistSetContent( session, filename );
836
837        /* give the client a response */
838        tr_bencDictAddInt( data->args_out, "blocklist-size", ruleCount );
839        tr_snprintf( result, sizeof( result ), "success" );
840
841        /* cleanup */
842        unlink( filename );
843        tr_free( filename );
844    }
845
846    tr_idle_function_done( data, result );
847}
848
849static const char*
850blocklistUpdate( tr_session               * session,
851                 tr_benc                  * args_in UNUSED,
852                 tr_benc                  * args_out UNUSED,
853                 struct tr_rpc_idle_data  * idle_data )
854{
855    const char * url = "http://update.transmissionbt.com/level1";
856    tr_webRun( session, url, NULL, gotNewBlocklist, idle_data );
857    return NULL;
858}
859
860/***
861****
862***/
863
864static void
865addTorrentImpl( struct tr_rpc_idle_data * data, tr_ctor * ctor )
866{
867    int err = 0;
868    const char * result = NULL;
869    tr_torrent * tor = tr_torrentNew( ctor, &err );
870
871    tr_ctorFree( ctor );
872
873    if( tor )
874    {
875        tr_benc fields;
876        tr_bencInitList( &fields, 3 );
877        tr_bencListAddStr( &fields, "id" );
878        tr_bencListAddStr( &fields, "name" );
879        tr_bencListAddStr( &fields, "hashString" );
880        addInfo( tor, tr_bencDictAdd( data->args_out, "torrent-added" ), &fields );
881        notify( data->session, TR_RPC_TORRENT_ADDED, tor );
882        tr_bencFree( &fields );
883    }
884    else if( err == TR_EDUPLICATE )
885    {
886        result = "duplicate torrent";
887    }
888    else if( err == TR_EINVALID )
889    {
890        result = "invalid or corrupt torrent file";
891    }
892
893    tr_idle_function_done( data, result );
894}
895
896
897struct add_torrent_idle_data
898{
899    struct tr_rpc_idle_data * data;
900    tr_ctor * ctor;
901};
902
903static void
904gotMetadataFromURL( tr_session       * session UNUSED,
905                    long               response_code,
906                    const void       * response,
907                    size_t             response_byte_count,
908                    void             * user_data )
909{
910    struct add_torrent_idle_data * data = user_data;
911
912    dbgmsg( "torrentAdd: HTTP response code was %ld (%s); response length was %zu bytes",
913            response_code, tr_webGetResponseStr( response_code ), response_byte_count );
914
915    if( response_code == 200 )
916    {
917        tr_ctorSetMetainfo( data->ctor, response, response_byte_count );
918        addTorrentImpl( data->data, data->ctor );
919    }
920    else
921    {
922        char result[1024];
923        tr_snprintf( result, sizeof( result ), "http error %ld: %s",
924                     response_code, tr_webGetResponseStr( response_code ) );
925        tr_idle_function_done( data->data, result );
926    }
927
928    tr_free( data );
929}
930
931static tr_bool
932isCurlURL( const char * filename )
933{
934    if( filename == NULL )
935        return FALSE;
936
937    return ( strstr( filename, "ftp://" ) != NULL )
938        || ( strstr( filename, "http://" ) != NULL )
939        || ( strstr( filename, "https://" ) != NULL );
940}
941
942static tr_file_index_t*
943fileListFromList( tr_benc * list, tr_file_index_t * setmeCount )
944{
945    size_t i;
946    const size_t childCount = tr_bencListSize( list );
947    tr_file_index_t n = 0;
948    tr_file_index_t * files = tr_new0( tr_file_index_t, childCount );
949
950    for( i=0; i<childCount; ++i ) {
951        int64_t intVal;
952        if( tr_bencGetInt( tr_bencListChild( list, i ), &intVal ) )
953            files[n++] = (tr_file_index_t)intVal;
954    }
955
956    *setmeCount = n;
957    return files;
958}
959
960static const char*
961torrentAdd( tr_session               * session,
962            tr_benc                  * args_in,
963            tr_benc                  * args_out UNUSED,
964            struct tr_rpc_idle_data  * idle_data )
965{
966    const char * filename = NULL;
967    const char * metainfo_base64 = NULL;
968
969    assert( idle_data != NULL );
970
971    tr_bencDictFindStr( args_in, "filename", &filename );
972    tr_bencDictFindStr( args_in, "metainfo", &metainfo_base64 );
973    if( !filename && !metainfo_base64 )
974        return "no filename or metainfo specified";
975    else
976    {
977        int64_t      i;
978        tr_bool      boolVal;
979        const char * str;
980        tr_benc    * l;
981        tr_ctor    * ctor = tr_ctorNew( session );
982
983        /* set the optional arguments */
984
985        if( tr_bencDictFindStr( args_in, "download-dir", &str ) )
986            tr_ctorSetDownloadDir( ctor, TR_FORCE, str );
987
988        if( tr_bencDictFindBool( args_in, "paused", &boolVal ) )
989            tr_ctorSetPaused( ctor, TR_FORCE, boolVal );
990
991        if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
992            tr_ctorSetPeerLimit( ctor, TR_FORCE, i );
993
994        if( tr_bencDictFindList( args_in, "files-unwanted", &l ) ) {
995            tr_file_index_t fileCount;
996            tr_file_index_t * files = fileListFromList( l, &fileCount );
997            tr_ctorSetFilesWanted( ctor, files, fileCount, FALSE );
998            tr_free( files );
999        }
1000        if( tr_bencDictFindList( args_in, "files-wanted", &l ) ) {
1001            tr_file_index_t fileCount;
1002            tr_file_index_t * files = fileListFromList( l, &fileCount );
1003            tr_ctorSetFilesWanted( ctor, files, fileCount, TRUE );
1004            tr_free( files );
1005        }
1006
1007        if( tr_bencDictFindList( args_in, "priority-low", &l ) ) {
1008            tr_file_index_t fileCount;
1009            tr_file_index_t * files = fileListFromList( l, &fileCount );
1010            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_LOW );
1011            tr_free( files );
1012        }
1013        if( tr_bencDictFindList( args_in, "priority-normal", &l ) ) {
1014            tr_file_index_t fileCount;
1015            tr_file_index_t * files = fileListFromList( l, &fileCount );
1016            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_NORMAL );
1017            tr_free( files );
1018        }
1019        if( tr_bencDictFindList( args_in, "priority-high", &l ) ) {
1020            tr_file_index_t fileCount;
1021            tr_file_index_t * files = fileListFromList( l, &fileCount );
1022            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_HIGH );
1023            tr_free( files );
1024        }
1025
1026        dbgmsg( "torrentAdd: filename is \"%s\"", filename );
1027
1028        if( isCurlURL( filename ) )
1029        {
1030            struct add_torrent_idle_data * d = tr_new0( struct add_torrent_idle_data, 1 );
1031            d->data = idle_data;
1032            d->ctor = ctor;
1033            tr_webRun( session, filename, NULL, gotMetadataFromURL, d );
1034        }
1035        else
1036        {
1037            if( filename != NULL )
1038                tr_ctorSetMetainfoFromFile( ctor, filename );
1039            else {
1040                int len;
1041                char * metainfo = tr_base64_decode( metainfo_base64, -1,  &len );
1042                tr_ctorSetMetainfo( ctor, (uint8_t*)metainfo, len );
1043                tr_free( metainfo );
1044            }
1045            addTorrentImpl( idle_data, ctor );
1046        }
1047
1048    }
1049
1050    return NULL;
1051}
1052
1053/***
1054****
1055***/
1056
1057static const char*
1058sessionSet( tr_session               * session,
1059            tr_benc                  * args_in,
1060            tr_benc                  * args_out UNUSED,
1061            struct tr_rpc_idle_data  * idle_data )
1062{
1063    int64_t      i;
1064    double       d;
1065    tr_bool      boolVal;
1066    const char * str;
1067
1068    assert( idle_data == NULL );
1069
1070    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_UP, &i ) )
1071        tr_sessionSetAltSpeed( session, TR_UP, i );
1072    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_DOWN, &i ) )
1073        tr_sessionSetAltSpeed( session, TR_DOWN, i );
1074    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_ENABLED, &boolVal ) )
1075        tr_sessionUseAltSpeed( session, boolVal );
1076    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &i ) )
1077        tr_sessionSetAltSpeedBegin( session, i );
1078    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_END, &i ) )
1079        tr_sessionSetAltSpeedEnd( session, i );
1080    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &i ) )
1081        tr_sessionSetAltSpeedDay( session, i );
1082    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &boolVal ) )
1083        tr_sessionUseAltSpeedTime( session, boolVal );
1084    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_BLOCKLIST_ENABLED, &boolVal ) )
1085        tr_blocklistSetEnabled( session, boolVal );
1086    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1087        tr_sessionSetDownloadDir( session, str );
1088    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &i ) )
1089        tr_sessionSetPeerLimit( session, i );
1090    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i ) )
1091        tr_sessionSetPeerLimitPerTorrent( session, i );
1092    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) )
1093        tr_sessionSetPexEnabled( session, boolVal );
1094    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, &boolVal ) )
1095        tr_sessionSetPeerPortRandomOnStart( session, boolVal );
1096    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_PORT, &i ) )
1097        tr_sessionSetPeerPort( session, i );
1098    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) )
1099        tr_sessionSetPortForwardingEnabled( session, boolVal );
1100    if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
1101        tr_sessionSetRatioLimit( session, d );
1102    if( tr_bencDictFindBool( args_in, "seedRatioLimited", &boolVal ) )
1103        tr_sessionSetRatioLimited( session, boolVal );
1104    if( tr_bencDictFindInt( args_in, "speed-limit-down", &i ) )
1105        tr_sessionSetSpeedLimit( session, TR_DOWN, i );
1106    if( tr_bencDictFindBool( args_in, "speed-limit-down-enabled", &boolVal ) )
1107        tr_sessionLimitSpeed( session, TR_DOWN, boolVal );
1108    if( tr_bencDictFindInt( args_in, "speed-limit-up", &i ) )
1109        tr_sessionSetSpeedLimit( session, TR_UP, i );
1110    if( tr_bencDictFindBool( args_in, "speed-limit-up-enabled", &boolVal ) )
1111        tr_sessionLimitSpeed( session, TR_UP, boolVal );
1112    if( tr_bencDictFindStr( args_in, "encryption", &str ) ) {
1113        if( !strcmp( str, "required" ) )
1114            tr_sessionSetEncryption( session, TR_ENCRYPTION_REQUIRED );
1115        else if( !strcmp( str, "tolerated" ) )
1116            tr_sessionSetEncryption( session, TR_CLEAR_PREFERRED );
1117        else
1118            tr_sessionSetEncryption( session, TR_ENCRYPTION_PREFERRED );
1119    }
1120
1121    notify( session, TR_RPC_SESSION_CHANGED, NULL );
1122
1123    return NULL;
1124}
1125
1126static const char*
1127sessionStats( tr_session               * session,
1128              tr_benc                  * args_in UNUSED,
1129              tr_benc                  * args_out,
1130              struct tr_rpc_idle_data  * idle_data )
1131{
1132    int running = 0;
1133    int total = 0;
1134    tr_benc * d; 
1135    tr_session_stats currentStats = { 0.0f, 0, 0, 0, 0, 0 }; 
1136    tr_session_stats cumulativeStats = { 0.0f, 0, 0, 0, 0, 0 }; 
1137    tr_torrent * tor = NULL;
1138
1139    assert( idle_data == NULL );
1140
1141    while(( tor = tr_torrentNext( session, tor ))) {
1142        ++total;
1143        if( tor->isRunning )
1144            ++running;
1145    }
1146
1147    tr_sessionGetStats( session, &currentStats ); 
1148    tr_sessionGetCumulativeStats( session, &cumulativeStats ); 
1149
1150    tr_bencDictAddInt( args_out, "activeTorrentCount", running );
1151    tr_bencDictAddInt( args_out, "downloadSpeed", (int)( tr_sessionGetPieceSpeed( session, TR_DOWN ) * 1024 ) );
1152    tr_bencDictAddInt( args_out, "pausedTorrentCount", total - running );
1153    tr_bencDictAddInt( args_out, "torrentCount", total );
1154    tr_bencDictAddInt( args_out, "uploadSpeed", (int)( tr_sessionGetPieceSpeed( session, TR_UP ) * 1024 ) );
1155
1156    d = tr_bencDictAddDict( args_out, "cumulative-stats", 5 ); 
1157    tr_bencDictAddInt( d, "downloadedBytes", cumulativeStats.downloadedBytes ); 
1158    tr_bencDictAddInt( d, "filesAdded", cumulativeStats.filesAdded ); 
1159    tr_bencDictAddInt( d, "secondsActive", cumulativeStats.secondsActive ); 
1160    tr_bencDictAddInt( d, "sessionCount", cumulativeStats.sessionCount ); 
1161    tr_bencDictAddInt( d, "uploadedBytes", cumulativeStats.uploadedBytes ); 
1162
1163    d = tr_bencDictAddDict( args_out, "current-stats", 5 ); 
1164    tr_bencDictAddInt( d, "downloadedBytes", currentStats.downloadedBytes ); 
1165    tr_bencDictAddInt( d, "filesAdded", currentStats.filesAdded ); 
1166    tr_bencDictAddInt( d, "secondsActive", currentStats.secondsActive ); 
1167    tr_bencDictAddInt( d, "sessionCount", currentStats.sessionCount ); 
1168    tr_bencDictAddInt( d, "uploadedBytes", currentStats.uploadedBytes ); 
1169
1170    return NULL;
1171}
1172
1173static const char*
1174sessionGet( tr_session               * s,
1175            tr_benc                  * args_in UNUSED,
1176            tr_benc                  * args_out,
1177            struct tr_rpc_idle_data  * idle_data )
1178{
1179    const char * str;
1180    tr_benc *    d = args_out;
1181
1182    assert( idle_data == NULL );
1183    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP, tr_sessionGetAltSpeed(s,TR_UP) );
1184    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN, tr_sessionGetAltSpeed(s,TR_DOWN) );
1185    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED, tr_sessionUsesAltSpeed(s) );
1186    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, tr_sessionGetAltSpeedBegin(s) );
1187    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,tr_sessionGetAltSpeedEnd(s) );
1188    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,tr_sessionGetAltSpeedDay(s) );
1189    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, tr_sessionUsesAltSpeedTime(s) );
1190    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, tr_blocklistIsEnabled( s ) );
1191    tr_bencDictAddInt ( d, "blocklist-size", tr_blocklistGetRuleCount( s ) );
1192    tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR, tr_sessionGetDownloadDir( s ) );
1193    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, tr_sessionGetPeerLimit( s ) );
1194    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT, tr_sessionGetPeerLimitPerTorrent( s ) );
1195    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED, tr_sessionIsPexEnabled( s ) );
1196    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT, tr_sessionGetPeerPort( s ) );
1197    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, tr_sessionGetPeerPortRandomOnStart( s ) );
1198    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) );
1199    tr_bencDictAddInt ( d, "rpc-version", 5 );
1200    tr_bencDictAddInt ( d, "rpc-version-minimum", 1 );
1201    tr_bencDictAddReal( d, "seedRatioLimit", tr_sessionGetRatioLimit( s ) );
1202    tr_bencDictAddBool( d, "seedRatioLimited", tr_sessionIsRatioLimited( s ) );
1203    tr_bencDictAddInt ( d, "speed-limit-up", tr_sessionGetSpeedLimit( s, TR_UP ) );
1204    tr_bencDictAddBool( d, "speed-limit-up-enabled", tr_sessionIsSpeedLimited( s, TR_UP ) );
1205    tr_bencDictAddInt ( d, "speed-limit-down", tr_sessionGetSpeedLimit( s, TR_DOWN ) );
1206    tr_bencDictAddBool( d, "speed-limit-down-enabled", tr_sessionIsSpeedLimited( s, TR_DOWN ) );
1207    tr_bencDictAddStr ( d, "version", LONG_VERSION_STRING );
1208    switch( tr_sessionGetEncryption( s ) ) {
1209        case TR_CLEAR_PREFERRED: str = "tolerated"; break;
1210        case TR_ENCRYPTION_REQUIRED: str = "required"; break; 
1211        default: str = "preferred"; break;
1212    }
1213    tr_bencDictAddStr( d, "encryption", str );
1214
1215    return NULL;
1216}
1217
1218/***
1219****
1220***/
1221
1222typedef const char* ( *handler )( tr_session*, tr_benc*, tr_benc*, struct tr_rpc_idle_data * );
1223
1224static struct method
1225{
1226    const char *  name;
1227    tr_bool       immediate;
1228    handler       func;
1229}
1230methods[] =
1231{
1232    { "port-test",          FALSE, portTest            },
1233    { "blocklist-update",   FALSE, blocklistUpdate     },
1234    { "session-get",        TRUE,  sessionGet          },
1235    { "session-set",        TRUE,  sessionSet          },
1236    { "session-stats",      TRUE,  sessionStats        },
1237    { "torrent-add",        FALSE, torrentAdd          },
1238    { "torrent-get",        TRUE,  torrentGet          },
1239    { "torrent-remove",     TRUE,  torrentRemove       },
1240    { "torrent-set",        TRUE,  torrentSet          },
1241    { "torrent-start",      TRUE,  torrentStart        },
1242    { "torrent-stop",       TRUE,  torrentStop         },
1243    { "torrent-verify",     TRUE,  torrentVerify       },
1244    { "torrent-reannounce", TRUE,  torrentReannounce   }
1245};
1246
1247static void
1248noop_response_callback( tr_session * session UNUSED,
1249                        const char * response UNUSED,
1250                        size_t       response_len UNUSED,
1251                        void       * user_data UNUSED )
1252{
1253}
1254
1255static void
1256request_exec( tr_session             * session,
1257              tr_benc                * request,
1258              tr_rpc_response_func     callback,
1259              void                   * callback_user_data )
1260{
1261    int i;
1262    const char * str;
1263    tr_benc * args_in = tr_bencDictFind( request, "arguments" );
1264    const char * result = NULL;
1265
1266    if( callback == NULL )
1267        callback = noop_response_callback;
1268
1269    /* parse the request */
1270    if( !tr_bencDictFindStr( request, "method", &str ) )
1271        result = "no method name";
1272    else {
1273        const int n = TR_N_ELEMENTS( methods );
1274        for( i = 0; i < n; ++i )
1275            if( !strcmp( str, methods[i].name ) )
1276                break;
1277        if( i ==n )
1278            result = "method name not recognized";
1279    }
1280
1281    /* if we couldn't figure out which method to use, return an error */
1282    if( result != NULL )
1283    {
1284        int64_t tag;
1285        tr_benc response;
1286        struct evbuffer * buf = tr_getBuffer( );
1287
1288        tr_bencInitDict( &response, 3 );
1289        tr_bencDictAddDict( &response, "arguments", 0 );
1290        tr_bencDictAddStr( &response, "result", result );
1291        if( tr_bencDictFindInt( request, "tag", &tag ) )
1292            tr_bencDictAddInt( &response, "tag", tag );
1293        tr_bencSaveAsJSON( &response, buf );
1294        (*callback)( session, (const char*)EVBUFFER_DATA(buf),
1295                     EVBUFFER_LENGTH( buf ), callback_user_data );
1296
1297        tr_releaseBuffer( buf );
1298        tr_bencFree( &response );
1299    }
1300    else if( methods[i].immediate )
1301    {
1302        int64_t tag;
1303        tr_benc response;
1304        tr_benc * args_out;
1305        struct evbuffer * buf = tr_getBuffer( );
1306
1307        tr_bencInitDict( &response, 3 );
1308        args_out = tr_bencDictAddDict( &response, "arguments", 0 );
1309        result = (*methods[i].func)( session, args_in, args_out, NULL );
1310        if( result == NULL )
1311            result = "success";
1312        tr_bencDictAddStr( &response, "result", result );
1313        if( tr_bencDictFindInt( request, "tag", &tag ) )
1314            tr_bencDictAddInt( &response, "tag", tag );
1315        tr_bencSaveAsJSON( &response, buf );
1316        (*callback)( session, (const char*)EVBUFFER_DATA(buf),
1317                     EVBUFFER_LENGTH(buf), callback_user_data );
1318
1319        tr_releaseBuffer( buf );
1320        tr_bencFree( &response );
1321    }
1322    else
1323    {
1324        int64_t tag;
1325        struct tr_rpc_idle_data * data = tr_new0( struct tr_rpc_idle_data, 1 );
1326        data->session = session;
1327        data->response = tr_new0( tr_benc, 1 );
1328        tr_bencInitDict( data->response, 3 );
1329        if( tr_bencDictFindInt( request, "tag", &tag ) )
1330            tr_bencDictAddInt( data->response, "tag", tag );
1331        data->args_out = tr_bencDictAddDict( data->response, "arguments", 0 );
1332        data->callback = callback;
1333        data->callback_user_data = callback_user_data;
1334        (*methods[i].func)( session, args_in, data->args_out, data );
1335    }
1336}
1337
1338void
1339tr_rpc_request_exec_json( tr_session            * session,
1340                          const void            * request_json,
1341                          int                     request_len,
1342                          tr_rpc_response_func    callback,
1343                          void                  * callback_user_data )
1344{
1345    tr_benc top;
1346    int have_content;
1347
1348    if( request_len < 0 )
1349        request_len = strlen( request_json );
1350
1351    have_content = !tr_jsonParse( request_json, request_len, &top, NULL );
1352    request_exec( session, have_content ? &top : NULL, callback, callback_user_data );
1353
1354    if( have_content )
1355        tr_bencFree( &top );
1356}
1357
1358/**
1359 * Munge the URI into a usable form.
1360 *
1361 * We have very loose typing on this to make the URIs as simple as possible:
1362 * - anything not a 'tag' or 'method' is automatically in 'arguments'
1363 * - values that are all-digits are numbers
1364 * - values that are all-digits or commas are number lists
1365 * - all other values are strings
1366 */
1367void
1368tr_rpc_parse_list_str( tr_benc     * setme,
1369                       const char  * str,
1370                       int           len )
1371
1372{
1373    int valueCount;
1374    int * values = tr_parseNumberRange( str, len, &valueCount );
1375
1376    if( valueCount == 0 )
1377        tr_bencInitStr( setme, str, len );
1378    else if( valueCount == 1 )
1379        tr_bencInitInt( setme, values[0] );
1380    else {
1381        int i;
1382        tr_bencInitList( setme, valueCount );
1383        for( i=0; i<valueCount; ++i )
1384            tr_bencListAddInt( setme, values[i] );
1385    }
1386
1387    tr_free( values );
1388}
1389
1390void
1391tr_rpc_request_exec_uri( tr_session           * session,
1392                         const void           * request_uri,
1393                         int                    request_len,
1394                         tr_rpc_response_func   callback,
1395                         void                 * callback_user_data )
1396{
1397    tr_benc      top, * args;
1398    char *       request = tr_strndup( request_uri, request_len );
1399    const char * pch;
1400
1401    tr_bencInitDict( &top, 3 );
1402    args = tr_bencDictAddDict( &top, "arguments", 0 );
1403
1404    pch = strchr( request, '?' );
1405    if( !pch ) pch = request;
1406    while( pch )
1407    {
1408        const char * delim = strchr( pch, '=' );
1409        const char * next = strchr( pch, '&' );
1410        if( delim )
1411        {
1412            char *    key = tr_strndup( pch, delim - pch );
1413            int       isArg = strcmp( key, "method" ) && strcmp( key, "tag" );
1414            tr_benc * parent = isArg ? args : &top;
1415            tr_rpc_parse_list_str( tr_bencDictAdd( parent, key ),
1416                                  delim + 1,
1417                                  next ? (size_t)(
1418                                       next -
1419                                      ( delim + 1 ) ) : strlen( delim + 1 ) );
1420            tr_free( key );
1421        }
1422        pch = next ? next + 1 : NULL;
1423    }
1424
1425    request_exec( session, &top, callback, callback_user_data );
1426
1427    /* cleanup */
1428    tr_bencFree( &top );
1429    tr_free( request );
1430}
Note: See TracBrowser for help on using the repository browser.