source: trunk/libtransmission/rpcimpl.c @ 10635

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

(trunk) #3060 "Local Peer Discovery" -- in the code, rename LDS as LPD for Local Peer Discovery

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