source: trunk/libtransmission/rpcimpl.c @ 9928

Last change on this file since 9928 was 9928, checked in by charles, 12 years ago

(trunk) second half of r9927.... :/

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