source: trunk/libtransmission/rpcimpl.c @ 10437

Last change on this file since 10437 was 10437, checked in by livings124, 12 years ago

#1869 Move the finished state to libtransmission. This setting is now remembered between launches. This also causes torrents that hit the seed ratio to not have this setting changed to unlimited until start.

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