source: trunk/libtransmission/rpcimpl.c @ 8148

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

(trunk) fix another rpc seed-ratio problem reported by livings

  • Property svn:keywords set to Date Rev Author Id
File size: 48.3 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 8148 2009-04-05 15:37:45Z 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, "seedRatioLimit", &d ) )
746            tr_torrentSetRatioLimit( tor, d );
747        if( tr_bencDictFindInt( args_in, "seedRatioMode", &tmp ) )
748            tr_torrentSetRatioMode( tor, tmp );
749        notify( session, TR_RPC_TORRENT_CHANGED, tor );
750    }
751
752    tr_free( torrents );
753    return errmsg;
754}
755
756/***
757****
758***/
759
760static void
761gotNewBlocklist( tr_session       * session,
762                 long               response_code,
763                 const void       * response,
764                 size_t             response_byte_count,
765                 void             * user_data )
766{
767    char result[1024];
768    struct tr_rpc_idle_data * data = user_data;
769
770    if( response_code != 200 )
771    {
772        tr_snprintf( result, sizeof( result ), "http error %ld: %s",
773                     response_code, tr_webGetResponseStr( response_code ) );
774    }
775    else /* success */
776    {
777        int ruleCount;
778        char * filename = tr_buildPath( tr_sessionGetConfigDir( session ), "blocklist.tmp", NULL );
779        FILE * fp;
780
781        /* download a new blocklist */
782        fp = fopen( filename, "w+" );
783        fwrite( response, 1, response_byte_count, fp );
784        fclose( fp );
785
786        /* feed it to the session */
787        ruleCount = tr_blocklistSetContent( session, filename );
788
789        /* give the client a response */
790        tr_bencDictAddInt( data->args_out, "blocklist-size", ruleCount );
791        tr_snprintf( result, sizeof( result ), "success" );
792
793        /* cleanup */
794        unlink( filename );
795        tr_free( filename );
796    }
797
798    tr_idle_function_done( data, result );
799}
800
801static const char*
802blocklistUpdate( tr_session               * session,
803                 tr_benc                  * args_in UNUSED,
804                 tr_benc                  * args_out UNUSED,
805                 struct tr_rpc_idle_data  * idle_data )
806{
807    /* FIXME: use this url after the website's updated */
808    /* const char * url = "http://update.transmissionbt.com/level1"; */
809    const char * url = "http://download.m0k.org/transmission/files/level1";
810    tr_webRun( session, url, NULL, gotNewBlocklist, idle_data );
811    return NULL;
812}
813
814/***
815****
816***/
817
818static void
819addTorrentImpl( struct tr_rpc_idle_data * data, tr_ctor * ctor )
820{
821    int err = 0;
822    const char * result = NULL;
823    tr_torrent * tor = tr_torrentNew( ctor, &err );
824
825    tr_ctorFree( ctor );
826
827    if( tor )
828    {
829        tr_benc fields;
830        tr_bencInitList( &fields, 3 );
831        tr_bencListAddStr( &fields, "id" );
832        tr_bencListAddStr( &fields, "name" );
833        tr_bencListAddStr( &fields, "hashString" );
834        addInfo( tor, tr_bencDictAdd( data->args_out, "torrent-added" ), &fields );
835        notify( data->session, TR_RPC_TORRENT_ADDED, tor );
836        tr_bencFree( &fields );
837    }
838    else if( err == TR_EDUPLICATE )
839    {
840        result = "duplicate torrent";
841    }
842    else if( err == TR_EINVALID )
843    {
844        result = "invalid or corrupt torrent file";
845    }
846
847    tr_idle_function_done( data, result );
848}
849
850
851struct add_torrent_idle_data
852{
853    struct tr_rpc_idle_data * data;
854    tr_ctor * ctor;
855};
856
857static void
858gotMetadataFromURL( tr_session       * session UNUSED,
859                    long               response_code,
860                    const void       * response,
861                    size_t             response_byte_count,
862                    void             * user_data )
863{
864    struct add_torrent_idle_data * data = user_data;
865
866    dbgmsg( "torrentAdd: HTTP response code was %ld (%s); response length was %zu bytes",
867            response_code, tr_webGetResponseStr( response_code ), response_byte_count );
868
869    if( response_code == 200 )
870    {
871        tr_ctorSetMetainfo( data->ctor, response, response_byte_count );
872        addTorrentImpl( data->data, data->ctor );
873    }
874    else
875    {
876        char result[1024];
877        tr_snprintf( result, sizeof( result ), "http error %ld: %s",
878                     response_code, tr_webGetResponseStr( response_code ) );
879        tr_idle_function_done( data->data, result );
880    }
881
882    tr_free( data );
883}
884
885static tr_bool
886isCurlURL( const char * filename )
887{
888    if( filename == NULL )
889        return FALSE;
890
891    return ( strstr( filename, "ftp://" ) != NULL )
892        || ( strstr( filename, "http://" ) != NULL )
893        || ( strstr( filename, "https://" ) != NULL );
894}
895
896static tr_file_index_t*
897fileListFromList( tr_benc * list, tr_file_index_t * setmeCount )
898{
899    size_t i;
900    const size_t childCount = tr_bencListSize( list );
901    tr_file_index_t n = 0;
902    tr_file_index_t * files = tr_new0( tr_file_index_t, childCount );
903
904    for( i=0; i<childCount; ++i ) {
905        int64_t intVal;
906        if( tr_bencGetInt( tr_bencListChild( list, i ), &intVal ) )
907            files[n++] = (tr_file_index_t)intVal;
908    }
909
910    *setmeCount = n;
911    return files;
912}
913
914static const char*
915torrentAdd( tr_session               * session,
916            tr_benc                  * args_in,
917            tr_benc                  * args_out UNUSED,
918            struct tr_rpc_idle_data  * idle_data )
919{
920    const char * filename = NULL;
921    const char * metainfo_base64 = NULL;
922
923    assert( idle_data != NULL );
924
925    tr_bencDictFindStr( args_in, "filename", &filename );
926    tr_bencDictFindStr( args_in, "metainfo", &metainfo_base64 );
927    if( !filename && !metainfo_base64 )
928        return "no filename or metainfo specified";
929    else
930    {
931        int64_t      i;
932        tr_bool      boolVal;
933        const char * str;
934        tr_benc    * l;
935        tr_ctor    * ctor = tr_ctorNew( session );
936
937        /* set the optional arguments */
938
939        if( tr_bencDictFindStr( args_in, "download-dir", &str ) )
940            tr_ctorSetDownloadDir( ctor, TR_FORCE, str );
941
942        if( tr_bencDictFindBool( args_in, "paused", &boolVal ) )
943            tr_ctorSetPaused( ctor, TR_FORCE, boolVal );
944
945        if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
946            tr_ctorSetPeerLimit( ctor, TR_FORCE, i );
947
948        if( tr_bencDictFindList( args_in, "files-unwanted", &l ) ) {
949            tr_file_index_t fileCount;
950            tr_file_index_t * files = fileListFromList( l, &fileCount );
951            tr_ctorSetFilesWanted( ctor, files, fileCount, FALSE );
952            tr_free( files );
953        }
954        if( tr_bencDictFindList( args_in, "files-wanted", &l ) ) {
955            tr_file_index_t fileCount;
956            tr_file_index_t * files = fileListFromList( l, &fileCount );
957            tr_ctorSetFilesWanted( ctor, files, fileCount, TRUE );
958            tr_free( files );
959        }
960
961        if( tr_bencDictFindList( args_in, "priority-low", &l ) ) {
962            tr_file_index_t fileCount;
963            tr_file_index_t * files = fileListFromList( l, &fileCount );
964            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_LOW );
965            tr_free( files );
966        }
967        if( tr_bencDictFindList( args_in, "priority-normal", &l ) ) {
968            tr_file_index_t fileCount;
969            tr_file_index_t * files = fileListFromList( l, &fileCount );
970            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_NORMAL );
971            tr_free( files );
972        }
973        if( tr_bencDictFindList( args_in, "priority-high", &l ) ) {
974            tr_file_index_t fileCount;
975            tr_file_index_t * files = fileListFromList( l, &fileCount );
976            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_HIGH );
977            tr_free( files );
978        }
979
980        dbgmsg( "torrentAdd: filename is \"%s\"", filename );
981
982        if( isCurlURL( filename ) )
983        {
984            struct add_torrent_idle_data * d = tr_new0( struct add_torrent_idle_data, 1 );
985            d->data = idle_data;
986            d->ctor = ctor;
987            tr_webRun( session, filename, NULL, gotMetadataFromURL, d );
988        }
989        else
990        {
991            if( filename != NULL )
992                tr_ctorSetMetainfoFromFile( ctor, filename );
993            else {
994                int len;
995                char * metainfo = tr_base64_decode( metainfo_base64, -1,  &len );
996                tr_ctorSetMetainfo( ctor, (uint8_t*)metainfo, len );
997                tr_free( metainfo );
998            }
999            addTorrentImpl( idle_data, ctor );
1000        }
1001
1002    }
1003
1004    return NULL;
1005}
1006
1007/***
1008****
1009***/
1010
1011static const char*
1012sessionSet( tr_session               * session,
1013            tr_benc                  * args_in,
1014            tr_benc                  * args_out UNUSED,
1015            struct tr_rpc_idle_data  * idle_data )
1016{
1017    int64_t      i;
1018    double       d;
1019    tr_bool      boolVal;
1020    const char * str;
1021
1022    assert( idle_data == NULL );
1023
1024    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_UP, &i ) )
1025        tr_sessionSetAltSpeed( session, TR_UP, i );
1026    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_DOWN, &i ) )
1027        tr_sessionSetAltSpeed( session, TR_DOWN, i );
1028    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_ENABLED, &boolVal ) )
1029        tr_sessionUseAltSpeed( session, boolVal );
1030    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &i ) )
1031        tr_sessionSetAltSpeedBegin( session, i );
1032    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_END, &i ) )
1033        tr_sessionSetAltSpeedEnd( session, i );
1034    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &i ) )
1035        tr_sessionSetAltSpeedDay( session, i );
1036    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &boolVal ) )
1037        tr_sessionUseAltSpeedTime( session, boolVal );
1038    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_BLOCKLIST_ENABLED, &boolVal ) )
1039        tr_blocklistSetEnabled( session, boolVal );
1040    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1041        tr_sessionSetDownloadDir( session, str );
1042    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &i ) )
1043        tr_sessionSetPeerLimit( session, i );
1044    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i ) )
1045        tr_sessionSetPeerLimitPerTorrent( session, i );
1046    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) )
1047        tr_sessionSetPexEnabled( session, boolVal );
1048    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, &boolVal ) )
1049        tr_sessionSetPeerPortRandomOnStart( session, boolVal );
1050    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_PORT, &i ) )
1051        tr_sessionSetPeerPort( session, i );
1052    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) )
1053        tr_sessionSetPortForwardingEnabled( session, boolVal );
1054    if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
1055        tr_sessionSetRatioLimit( session, d );
1056    if( tr_bencDictFindBool( args_in, "seedRatioLimited", &boolVal ) )
1057        tr_sessionSetRatioLimited( session, boolVal );
1058    if( tr_bencDictFindInt( args_in, "speed-limit-down", &i ) )
1059        tr_sessionSetSpeedLimit( session, TR_DOWN, i );
1060    if( tr_bencDictFindBool( args_in, "speed-limit-down-enabled", &boolVal ) )
1061        tr_sessionLimitSpeed( session, TR_DOWN, boolVal );
1062    if( tr_bencDictFindInt( args_in, "speed-limit-up", &i ) )
1063        tr_sessionSetSpeedLimit( session, TR_UP, i );
1064    if( tr_bencDictFindBool( args_in, "speed-limit-up-enabled", &boolVal ) )
1065        tr_sessionLimitSpeed( session, TR_UP, boolVal );
1066    if( tr_bencDictFindStr( args_in, "encryption", &str ) ) {
1067        if( !strcmp( str, "required" ) )
1068            tr_sessionSetEncryption( session, TR_ENCRYPTION_REQUIRED );
1069        else if( !strcmp( str, "tolerated" ) )
1070            tr_sessionSetEncryption( session, TR_CLEAR_PREFERRED );
1071        else
1072            tr_sessionSetEncryption( session, TR_ENCRYPTION_PREFERRED );
1073    }
1074
1075    notify( session, TR_RPC_SESSION_CHANGED, NULL );
1076
1077    return NULL;
1078}
1079
1080static const char*
1081sessionStats( tr_session               * session,
1082              tr_benc                  * args_in UNUSED,
1083              tr_benc                  * args_out,
1084              struct tr_rpc_idle_data  * idle_data )
1085{
1086    int running = 0;
1087    int total = 0;
1088    tr_benc * d; 
1089    tr_session_stats currentStats = { 0.0f, 0, 0, 0, 0, 0 }; 
1090    tr_session_stats cumulativeStats = { 0.0f, 0, 0, 0, 0, 0 }; 
1091    tr_torrent * tor = NULL;
1092
1093    assert( idle_data == NULL );
1094
1095    while(( tor = tr_torrentNext( session, tor ))) {
1096        ++total;
1097        if( tor->isRunning )
1098            ++running;
1099    }
1100
1101    tr_sessionGetStats( session, &currentStats ); 
1102    tr_sessionGetCumulativeStats( session, &cumulativeStats ); 
1103
1104    tr_bencDictAddInt( args_out, "activeTorrentCount", running );
1105    tr_bencDictAddInt( args_out, "downloadSpeed", (int)( tr_sessionGetPieceSpeed( session, TR_DOWN ) * 1024 ) );
1106    tr_bencDictAddInt( args_out, "pausedTorrentCount", total - running );
1107    tr_bencDictAddInt( args_out, "torrentCount", total );
1108    tr_bencDictAddInt( args_out, "uploadSpeed", (int)( tr_sessionGetPieceSpeed( session, TR_UP ) * 1024 ) );
1109
1110    d = tr_bencDictAddDict( args_out, "cumulative-stats", 5 ); 
1111    tr_bencDictAddInt( d, "downloadedBytes", cumulativeStats.downloadedBytes ); 
1112    tr_bencDictAddInt( d, "filesAdded", cumulativeStats.filesAdded ); 
1113    tr_bencDictAddInt( d, "secondsActive", cumulativeStats.secondsActive ); 
1114    tr_bencDictAddInt( d, "sessionCount", cumulativeStats.sessionCount ); 
1115    tr_bencDictAddInt( d, "uploadedBytes", cumulativeStats.uploadedBytes ); 
1116
1117    d = tr_bencDictAddDict( args_out, "current-stats", 5 ); 
1118    tr_bencDictAddInt( d, "downloadedBytes", currentStats.downloadedBytes ); 
1119    tr_bencDictAddInt( d, "filesAdded", currentStats.filesAdded ); 
1120    tr_bencDictAddInt( d, "secondsActive", currentStats.secondsActive ); 
1121    tr_bencDictAddInt( d, "sessionCount", currentStats.sessionCount ); 
1122    tr_bencDictAddInt( d, "uploadedBytes", currentStats.uploadedBytes ); 
1123
1124    return NULL;
1125}
1126
1127static const char*
1128sessionGet( tr_session               * s,
1129            tr_benc                  * args_in UNUSED,
1130            tr_benc                  * args_out,
1131            struct tr_rpc_idle_data  * idle_data )
1132{
1133    const char * str;
1134    tr_benc *    d = args_out;
1135
1136    assert( idle_data == NULL );
1137    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP, tr_sessionGetAltSpeed(s,TR_UP) );
1138    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN, tr_sessionGetAltSpeed(s,TR_DOWN) );
1139    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED, tr_sessionUsesAltSpeed(s) );
1140    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, tr_sessionGetAltSpeedBegin(s) );
1141    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,tr_sessionGetAltSpeedEnd(s) );
1142    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,tr_sessionGetAltSpeedDay(s) );
1143    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, tr_sessionUsesAltSpeedTime(s) );
1144    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, tr_blocklistIsEnabled( s ) );
1145    tr_bencDictAddInt ( d, "blocklist-size", tr_blocklistGetRuleCount( s ) );
1146    tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR, tr_sessionGetDownloadDir( s ) );
1147    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, tr_sessionGetPeerLimit( s ) );
1148    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT, tr_sessionGetPeerLimitPerTorrent( s ) );
1149    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED, tr_sessionIsPexEnabled( s ) );
1150    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT, tr_sessionGetPeerPort( s ) );
1151    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, tr_sessionGetPeerPortRandomOnStart( s ) );
1152    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) );
1153    tr_bencDictAddInt ( d, "rpc-version", 4 );
1154    tr_bencDictAddInt ( d, "rpc-version-minimum", 1 );
1155    tr_bencDictAddReal( d, "seedRatioLimit", tr_sessionGetRatioLimit( s ) );
1156    tr_bencDictAddBool( d, "seedRatioLimited", tr_sessionIsRatioLimited( s ) );
1157    tr_bencDictAddInt ( d, "speed-limit-up", tr_sessionGetSpeedLimit( s, TR_UP ) );
1158    tr_bencDictAddBool( d, "speed-limit-up-enabled", tr_sessionIsSpeedLimited( s, TR_UP ) );
1159    tr_bencDictAddInt ( d, "speed-limit-down", tr_sessionGetSpeedLimit( s, TR_DOWN ) );
1160    tr_bencDictAddBool( d, "speed-limit-down-enabled", tr_sessionIsSpeedLimited( s, TR_DOWN ) );
1161    tr_bencDictAddStr ( d, "version", LONG_VERSION_STRING );
1162    switch( tr_sessionGetEncryption( s ) ) {
1163        case TR_CLEAR_PREFERRED: str = "tolerated"; break;
1164        case TR_ENCRYPTION_REQUIRED: str = "required"; break; 
1165        default: str = "preferred"; break;
1166    }
1167    tr_bencDictAddStr( d, "encryption", str );
1168
1169    return NULL;
1170}
1171
1172/***
1173****
1174***/
1175
1176typedef const char* ( *handler )( tr_session*, tr_benc*, tr_benc*, struct tr_rpc_idle_data * );
1177
1178static struct method
1179{
1180    const char *  name;
1181    tr_bool       immediate;
1182    handler       func;
1183}
1184methods[] =
1185{
1186    { "blocklist-update",   FALSE, blocklistUpdate     },
1187    { "session-get",        TRUE,  sessionGet          },
1188    { "session-set",        TRUE,  sessionSet          },
1189    { "session-stats",      TRUE,  sessionStats        },
1190    { "torrent-add",        FALSE, torrentAdd          },
1191    { "torrent-get",        TRUE,  torrentGet          },
1192    { "torrent-remove",     TRUE,  torrentRemove       },
1193    { "torrent-set",        TRUE,  torrentSet          },
1194    { "torrent-start",      TRUE,  torrentStart        },
1195    { "torrent-stop",       TRUE,  torrentStop         },
1196    { "torrent-verify",     TRUE,  torrentVerify       },
1197    { "torrent-reannounce", TRUE,  torrentReannounce   }
1198};
1199
1200static void
1201noop_response_callback( tr_session * session UNUSED,
1202                        const char * response UNUSED,
1203                        size_t       response_len UNUSED,
1204                        void       * user_data UNUSED )
1205{
1206}
1207
1208static void
1209request_exec( tr_session             * session,
1210              tr_benc                * request,
1211              tr_rpc_response_func     callback,
1212              void                   * callback_user_data )
1213{
1214    int i;
1215    const char * str;
1216    tr_benc * args_in = tr_bencDictFind( request, "arguments" );
1217    const char * result = NULL;
1218
1219    if( callback == NULL )
1220        callback = noop_response_callback;
1221
1222    /* parse the request */
1223    if( !tr_bencDictFindStr( request, "method", &str ) )
1224        result = "no method name";
1225    else {
1226        const int n = TR_N_ELEMENTS( methods );
1227        for( i = 0; i < n; ++i )
1228            if( !strcmp( str, methods[i].name ) )
1229                break;
1230        if( i ==n )
1231            result = "method name not recognized";
1232    }
1233
1234    /* if we couldn't figure out which method to use, return an error */
1235    if( result != NULL )
1236    {
1237        int64_t tag;
1238        tr_benc response;
1239        struct evbuffer * buf = tr_getBuffer( );
1240
1241        tr_bencInitDict( &response, 3 );
1242        tr_bencDictAddDict( &response, "arguments", 0 );
1243        tr_bencDictAddStr( &response, "result", result );
1244        if( tr_bencDictFindInt( request, "tag", &tag ) )
1245            tr_bencDictAddInt( &response, "tag", tag );
1246        tr_bencSaveAsJSON( &response, buf );
1247        (*callback)( session, (const char*)EVBUFFER_DATA(buf),
1248                     EVBUFFER_LENGTH( buf ), callback_user_data );
1249
1250        tr_releaseBuffer( buf );
1251        tr_bencFree( &response );
1252    }
1253    else if( methods[i].immediate )
1254    {
1255        int64_t tag;
1256        tr_benc response;
1257        tr_benc * args_out;
1258        struct evbuffer * buf = tr_getBuffer( );
1259
1260        tr_bencInitDict( &response, 3 );
1261        args_out = tr_bencDictAddDict( &response, "arguments", 0 );
1262        result = (*methods[i].func)( session, args_in, args_out, NULL );
1263        if( result == NULL )
1264            result = "success";
1265        tr_bencDictAddStr( &response, "result", result );
1266        if( tr_bencDictFindInt( request, "tag", &tag ) )
1267            tr_bencDictAddInt( &response, "tag", tag );
1268        tr_bencSaveAsJSON( &response, buf );
1269        (*callback)( session, (const char*)EVBUFFER_DATA(buf),
1270                     EVBUFFER_LENGTH(buf), callback_user_data );
1271
1272        tr_releaseBuffer( buf );
1273        tr_bencFree( &response );
1274    }
1275    else
1276    {
1277        int64_t tag;
1278        struct tr_rpc_idle_data * data = tr_new0( struct tr_rpc_idle_data, 1 );
1279        data->session = session;
1280        data->response = tr_new0( tr_benc, 1 );
1281        tr_bencInitDict( data->response, 3 );
1282        if( tr_bencDictFindInt( request, "tag", &tag ) )
1283            tr_bencDictAddInt( data->response, "tag", tag );
1284        data->args_out = tr_bencDictAddDict( data->response, "arguments", 0 );
1285        data->callback = callback;
1286        data->callback_user_data = callback_user_data;
1287        (*methods[i].func)( session, args_in, data->args_out, data );
1288    }
1289}
1290
1291void
1292tr_rpc_request_exec_json( tr_session            * session,
1293                          const void            * request_json,
1294                          int                     request_len,
1295                          tr_rpc_response_func    callback,
1296                          void                  * callback_user_data )
1297{
1298    tr_benc top;
1299    int have_content;
1300
1301    if( request_len < 0 )
1302        request_len = strlen( request_json );
1303
1304    have_content = !tr_jsonParse( request_json, request_len, &top, NULL );
1305    request_exec( session, have_content ? &top : NULL, callback, callback_user_data );
1306
1307    if( have_content )
1308        tr_bencFree( &top );
1309}
1310
1311/**
1312 * Munge the URI into a usable form.
1313 *
1314 * We have very loose typing on this to make the URIs as simple as possible:
1315 * - anything not a 'tag' or 'method' is automatically in 'arguments'
1316 * - values that are all-digits are numbers
1317 * - values that are all-digits or commas are number lists
1318 * - all other values are strings
1319 */
1320void
1321tr_rpc_parse_list_str( tr_benc     * setme,
1322                       const char  * str,
1323                       int           len )
1324
1325{
1326    int valueCount;
1327    int * values = tr_parseNumberRange( str, len, &valueCount );
1328
1329    if( valueCount == 0 )
1330        tr_bencInitStr( setme, str, len );
1331    else if( valueCount == 1 )
1332        tr_bencInitInt( setme, values[0] );
1333    else {
1334        int i;
1335        tr_bencInitList( setme, valueCount );
1336        for( i=0; i<valueCount; ++i )
1337            tr_bencListAddInt( setme, values[i] );
1338    }
1339
1340    tr_free( values );
1341}
1342
1343void
1344tr_rpc_request_exec_uri( tr_session           * session,
1345                         const void           * request_uri,
1346                         int                    request_len,
1347                         tr_rpc_response_func   callback,
1348                         void                 * callback_user_data )
1349{
1350    tr_benc      top, * args;
1351    char *       request = tr_strndup( request_uri, request_len );
1352    const char * pch;
1353
1354    tr_bencInitDict( &top, 3 );
1355    args = tr_bencDictAddDict( &top, "arguments", 0 );
1356
1357    pch = strchr( request, '?' );
1358    if( !pch ) pch = request;
1359    while( pch )
1360    {
1361        const char * delim = strchr( pch, '=' );
1362        const char * next = strchr( pch, '&' );
1363        if( delim )
1364        {
1365            char *    key = tr_strndup( pch, delim - pch );
1366            int       isArg = strcmp( key, "method" ) && strcmp( key, "tag" );
1367            tr_benc * parent = isArg ? args : &top;
1368            tr_rpc_parse_list_str( tr_bencDictAdd( parent, key ),
1369                                  delim + 1,
1370                                  next ? (size_t)(
1371                                       next -
1372                                      ( delim + 1 ) ) : strlen( delim + 1 ) );
1373            tr_free( key );
1374        }
1375        pch = next ? next + 1 : NULL;
1376    }
1377
1378    request_exec( session, &top, callback, callback_user_data );
1379
1380    /* cleanup */
1381    tr_bencFree( &top );
1382    tr_free( request );
1383}
Note: See TracBrowser for help on using the repository browser.