source: trunk/libtransmission/rpcimpl.c @ 8132

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

(trunk)

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