source: trunk/libtransmission/rpc.c @ 6346

Last change on this file since 6346 was 6346, checked in by charles, 14 years ago

(rpc) allocate a bit in torrent-set/torrent-get for detailed peer information

  • Property svn:keywords set to Date Rev Author Id
File size: 24.5 KB
Line 
1/*
2 * This file Copyright (C) 2008 Charles Kerr <charles@rebelbase.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: rpc.c 6346 2008-07-16 20:31:17Z charles $
11 */
12
13#include <assert.h>
14#include <ctype.h> /* isdigit */
15#include <stdlib.h> /* strtol */
16#include <string.h> /* strcmp */
17
18#include "transmission.h"
19#include "bencode.h"
20#include "ratecontrol.h"
21#include "rpc.h"
22#include "json.h"
23#include "session.h"
24#include "torrent.h"
25#include "utils.h"
26
27#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) )
28
29/***
30****
31***/
32
33static void
34notify( tr_handle * session, int type, tr_torrent * tor )
35{
36    if( session->rpc_func != NULL )
37        session->rpc_func( session, type, tor, session->rpc_func_user_data );
38}
39
40/***
41****
42***/
43
44static tr_torrent **
45getTorrents( tr_handle * handle, tr_benc * args, int * setmeCount )
46{
47    int torrentCount = 0;
48    int64_t id;
49    tr_torrent ** torrents = NULL;
50    tr_benc * ids;
51
52    if( tr_bencDictFindList( args, "ids", &ids ) )
53    {
54        int i;
55        const int n = tr_bencListSize( ids );
56
57        torrents = tr_new0( tr_torrent*, n );
58
59        for( i=0; i<n; ++i )
60        {
61            tr_torrent * tor = NULL;
62            tr_benc * node = tr_bencListChild( ids, i );
63            int64_t id;
64            const char * str;
65            if( tr_bencGetInt( node, &id ) )
66                tor = tr_torrentFindFromId( handle, id );
67            else if( tr_bencGetStr( node, &str ) )
68                tor = tr_torrentFindFromHashString( handle, str );
69            if( tor )
70                torrents[torrentCount++] = tor;
71        }
72    }
73    else if( tr_bencDictFindInt( args, "ids", &id ) 
74          || tr_bencDictFindInt( args, "id", &id ) )
75    {
76        tr_torrent * tor;
77        torrents = tr_new0( tr_torrent*, 1 );
78        if(( tor = tr_torrentFindFromId( handle, id )))
79            torrents[torrentCount++] = tor;
80    }
81    else /* all of them */
82    {
83        tr_torrent * tor = NULL;
84        const int n = tr_sessionCountTorrents( handle );
85        torrents = tr_new0( tr_torrent*, n );
86        while(( tor = tr_torrentNext( handle, tor )))
87            torrents[torrentCount++] = tor;
88    }
89
90    *setmeCount = torrentCount;
91    return torrents;
92}
93
94static const char*
95torrentStart( tr_handle * h, tr_benc * args_in, tr_benc * args_out UNUSED )
96{
97    int i, torrentCount;
98    tr_torrent ** torrents = getTorrents( h, args_in, &torrentCount );
99    for( i=0; i<torrentCount; ++i )
100    {
101        tr_torrent * tor = torrents[i];
102        tr_torrentStart( tor );
103        notify( h, TR_RPC_TORRENT_STARTED, tor );
104    }
105    tr_free( torrents );
106    return NULL;
107}
108
109static const char*
110torrentStop( tr_handle * h, tr_benc * args_in, tr_benc * args_out UNUSED )
111{
112    int i, torrentCount;
113    tr_torrent ** torrents = getTorrents( h, args_in, &torrentCount );
114    for( i=0; i<torrentCount; ++i )
115    {
116        tr_torrent * tor = torrents[i];
117        tr_torrentStop( tor );
118        notify( h, TR_RPC_TORRENT_STOPPED, tor );
119    }
120    tr_free( torrents );
121    return NULL;
122}
123
124static const char*
125torrentRemove( tr_handle * h, tr_benc * args_in, tr_benc * args_out UNUSED )
126{
127    int i, torrentCount;
128    tr_torrent ** torrents = getTorrents( h, args_in, &torrentCount );
129    for( i=0; i<torrentCount; ++i )
130    {
131        tr_torrent * tor = torrents[i];
132        notify( h, TR_RPC_TORRENT_REMOVING, tor );
133        tr_torrentRemove( tor );
134    }
135    tr_free( torrents );
136    return NULL;
137}
138
139static const char*
140torrentVerify( tr_handle * h, tr_benc * args_in, tr_benc * args_out UNUSED )
141{
142    int i, torrentCount;
143    tr_torrent ** torrents = getTorrents( h, args_in, &torrentCount );
144    for( i=0; i<torrentCount; ++i )
145    {
146        tr_torrent * tor = torrents[i];
147        tr_torrentVerify( tor );
148        notify( h, TR_RPC_TORRENT_CHANGED, tor );
149    }
150    tr_free( torrents );
151    return NULL;
152}
153
154/***
155****
156***/
157
158static void
159addFiles( const tr_info * info, tr_benc * files )
160{
161    tr_file_index_t i;
162    for( i=0; i<info->fileCount; ++i )
163    {
164        const tr_file * file = &info->files[i];
165        tr_benc * d = tr_bencListAddDict( files, 2 );
166        tr_bencDictAddInt( d, "length", file->length );
167        tr_bencDictAddStr( d, "name", file->name );
168    }
169}
170
171static void
172addWebseeds( const tr_info * info, tr_benc * webseeds )
173{
174    int i;
175    for( i=0; i<info->webseedCount; ++i )
176        tr_bencListAddStr( webseeds, info->webseeds[i] );
177}
178
179static void
180addTrackers( const tr_info * info, tr_benc * trackers )
181{
182    int i;
183    for( i=0; i<info->trackerCount; ++i )
184    {
185        const tr_tracker_info * t = &info->trackers[i];
186        tr_benc * d = tr_bencListAddDict( trackers, 3 );
187        tr_bencDictAddStr( d, "announce", t->announce );
188        tr_bencDictAddStr( d, "scrape", t->scrape );
189        tr_bencDictAddInt( d, "tier", t->tier );
190    }
191}
192
193static void
194addInfo( const tr_torrent * tor, tr_benc * d, uint64_t fields )
195{
196    const tr_info * inf = tr_torrentInfo( tor );
197    const tr_stat * st = tr_torrentStat( (tr_torrent*)tor );
198
199    tr_bencInitDict( d, 64 );
200
201    if( fields & TR_RPC_TORRENT_ACTIVITY ) {
202        tr_bencDictAddInt( d, "desiredAvailable", st->desiredAvailable );
203        tr_bencDictAddInt( d, "eta", st->eta );
204        tr_bencDictAddInt( d, "peersConnected", st->peersConnected );
205        tr_bencDictAddInt( d, "peersGettingFromUs", st->peersGettingFromUs );
206        tr_bencDictAddInt( d, "peersSendingToUs", st->peersSendingToUs );
207        tr_bencDictAddInt( d, "rateDownload", (int)(st->rateDownload*1024) );
208        tr_bencDictAddInt( d, "rateUpload", (int)(st->rateUpload*1024) );
209        tr_bencDictAddDouble( d, "recheckProgress", st->recheckProgress );
210        tr_bencDictAddInt( d, "status", st->status );
211        tr_bencDictAddInt( d, "swarmSpeed", (int)(st->swarmSpeed*1024) );
212        tr_bencDictAddInt( d, "webseedsSendingToUs", st->webseedsSendingToUs );
213        tr_bencDictAddDouble( d, "uploadRatio", tr_getRatio( st->uploadedEver, st->downloadedEver ) );
214    }
215
216    if( fields & TR_RPC_TORRENT_ANNOUNCE ) {
217        tr_bencDictAddStr( d, "announceResponse", st->announceResponse );
218        tr_bencDictAddStr( d, "announceURL", st->announceURL );
219        tr_bencDictAddInt( d, "lastAnnounceTime", st->lastAnnounceTime );
220        tr_bencDictAddInt( d, "manualAnnounceTime", st->manualAnnounceTime );
221        tr_bencDictAddInt( d, "nextAnnounceTime", st->nextAnnounceTime );
222    }
223
224    if( fields & TR_RPC_TORRENT_ERROR ) {
225        tr_bencDictAddInt( d, "error", st->error );
226        tr_bencDictAddStr( d, "errorString", st->errorString );
227    }
228
229    if( fields & TR_RPC_TORRENT_FILES )
230        addFiles( inf, tr_bencDictAddList( d, "files", inf->fileCount ) );
231
232    if( fields & TR_RPC_TORRENT_HISTORY ) {
233        tr_bencDictAddInt( d, "activityDate", st->activityDate );
234        tr_bencDictAddInt( d, "addedDate", st->addedDate );
235        tr_bencDictAddInt( d, "corruptEver", st->corruptEver );
236        tr_bencDictAddInt( d, "doneDate", st->doneDate );
237        tr_bencDictAddInt( d, "downloadedEver", st->downloadedEver );
238        tr_bencDictAddInt( d, "startDate", st->startDate );
239        tr_bencDictAddInt( d, "uploadedEver", st->uploadedEver );
240    }
241
242    if( fields & TR_RPC_TORRENT_ID ) {
243        tr_bencDictAddInt( d, "id", st->id );
244        tr_bencDictAddStr( d, "hashString", tor->info.hashString );
245        tr_bencDictAddStr( d, "name", inf->name );
246    }
247
248    if( fields & TR_RPC_TORRENT_INFO ) {
249        tr_bencDictAddStr( d, "comment", inf->comment ? inf->comment : "" );
250        tr_bencDictAddStr( d, "creator", inf->creator ? inf->creator : "" );
251        tr_bencDictAddInt( d, "dateCreated", inf->dateCreated );
252        tr_bencDictAddInt( d, "isPrivate", tr_torrentIsPrivate( tor ) );
253        tr_bencDictAddInt( d, "pieceCount", inf->pieceCount );
254        tr_bencDictAddInt( d, "pieceSize", inf->pieceSize );
255    }
256
257    if( fields & TR_RPC_TORRENT_LIMITS ) {
258        tr_bencDictAddInt( d, "downloadLimit", tr_torrentGetSpeedLimit( tor, TR_DOWN ) );
259        tr_bencDictAddInt( d, "downloadLimitMode", tr_torrentGetSpeedMode( tor, TR_DOWN ) );
260        tr_bencDictAddInt( d, "maxConnectedPeers",  tr_torrentGetPeerLimit( tor ) );
261        tr_bencDictAddInt( d, "uploadLimit", tr_torrentGetSpeedLimit( tor, TR_UP ) );
262        tr_bencDictAddInt( d, "uploadLimitMode",   tr_torrentGetSpeedMode( tor, TR_UP ) );
263    }
264
265    if( fields & TR_RPC_TORRENT_PEER_STATS ) {
266        const int * f = st->peersFrom;
267        tr_bencDictAddInt( d, "fromCache",    f[TR_PEER_FROM_CACHE] );
268        tr_bencDictAddInt( d, "fromIncoming", f[TR_PEER_FROM_INCOMING] );
269        tr_bencDictAddInt( d, "fromPex",      f[TR_PEER_FROM_PEX] );
270        tr_bencDictAddInt( d, "fromTracker",  f[TR_PEER_FROM_TRACKER] );
271    }
272
273    if( fields & TR_RPC_TORRENT_PRIORITIES ) {
274        tr_file_index_t i;
275        tr_benc * p = tr_bencDictAddList( d, "priorities", inf->fileCount );
276        tr_benc * w = tr_bencDictAddList( d, "wanted", inf->fileCount );
277        for( i=0; i<inf->fileCount; ++i ) {
278            tr_bencListAddInt( p, inf->files[i].priority );
279            tr_bencListAddInt( w, inf->files[i].dnd ? 0 : 1 );
280        }
281    }
282
283    if( fields & TR_RPC_TORRENT_SCRAPE ) {
284        tr_bencDictAddInt( d, "lastScrapeTime", st->lastScrapeTime );
285        tr_bencDictAddInt( d, "nextScrapeTime", st->nextScrapeTime );
286        tr_bencDictAddStr( d, "scrapeResponse", st->scrapeResponse );
287        tr_bencDictAddStr( d, "scrapeURL", st->scrapeURL );
288    }
289
290    if( fields & TR_RPC_TORRENT_SIZE ) {
291        tr_bencDictAddInt( d, "haveUnchecked", st->haveUnchecked );
292        tr_bencDictAddInt( d, "haveValid", st->haveValid );
293        tr_bencDictAddInt( d, "leftUntilDone", st->leftUntilDone );
294        tr_bencDictAddInt( d, "sizeWhenDone", st->sizeWhenDone );
295        tr_bencDictAddInt( d, "totalSize", inf->totalSize );
296    }
297
298    if( fields & TR_RPC_TORRENT_TRACKER_STATS ) {
299        tr_bencDictAddInt( d, "leechers", st->leechers );
300        tr_bencDictAddInt( d, "peersKnown", st->peersKnown );
301        tr_bencDictAddInt( d, "seeders", st->seeders );
302        tr_bencDictAddInt( d, "timesCompleted", st->timesCompleted );
303    }
304
305    if( fields & TR_RPC_TORRENT_TRACKERS )
306        addTrackers( inf, tr_bencDictAddList( d, "trackers", inf->trackerCount ) );
307
308    if( fields & TR_RPC_TORRENT_WEBSEEDS )
309        addWebseeds( inf, tr_bencDictAddList( d, "webseeds", inf->trackerCount ) );
310}
311
312static const char*
313torrentGet( tr_handle * handle, tr_benc * args_in, tr_benc * args_out )
314{
315    int i, torrentCount;
316    tr_torrent ** torrents = getTorrents( handle, args_in, &torrentCount );
317    tr_benc * list = tr_bencDictAddList( args_out, "torrents", torrentCount );
318    int64_t fields = 0;
319
320    if( !tr_bencDictFindInt( args_in, "fields", &fields ) )
321        fields = ~(int64_t)0;
322    tr_bencDictAddInt( args_out, "fields", fields );
323
324    for( i=0; i<torrentCount; ++i )
325        addInfo( torrents[i], tr_bencListAdd( list ), fields );
326
327    tr_free( torrents );
328    return NULL;
329}
330
331/***
332****
333***/
334
335static void
336setFilePriorities( tr_torrent * tor, int priority, tr_benc * list )
337{
338    int i;
339    int64_t tmp;
340    int fileCount = 0;
341    const int n = tr_bencListSize( list );
342    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
343
344    if( n )
345    {
346        for( i=0; i<n; ++i )
347            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) )
348                if( 0<=tmp && tmp<tor->info.fileCount )
349                    files[fileCount++] = tmp;
350    }
351    else // if empty set, apply to all
352    {
353        tr_file_index_t t;
354        for( t=0; t<tor->info.fileCount; ++t )
355            files[fileCount++] = t;
356    }
357
358    if( fileCount )
359        tr_torrentSetFilePriorities( tor, files, fileCount, priority );
360
361    tr_free( files );
362}
363
364static void
365setFileDLs( tr_torrent * tor, int do_download, tr_benc * list )
366{
367    int i;
368    int64_t tmp;
369    int fileCount = 0;
370    const int n = tr_bencListSize( list );
371    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
372
373    if( n ) // if argument list, process them
374    {
375        for( i=0; i<n; ++i )
376            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) )
377                if( 0<=tmp && tmp<tor->info.fileCount )
378                    files[fileCount++] = tmp;
379    }
380    else // if empty set, apply to all
381    {
382        tr_file_index_t t;
383        for( t=0; t<tor->info.fileCount; ++t )
384            files[fileCount++] = t;
385    }
386
387    if( fileCount )
388        tr_torrentSetFileDLs( tor, files, fileCount, do_download );
389
390    tr_free( files );
391}
392
393static const char*
394torrentSet( tr_handle * h, tr_benc * args_in, tr_benc * args_out UNUSED )
395{
396    int i, torrentCount;
397    tr_torrent ** torrents = getTorrents( h, args_in, &torrentCount );
398
399    for( i=0; i<torrentCount; ++i )
400    {
401        int64_t tmp;
402        tr_benc * files;
403        tr_torrent * tor = torrents[i];
404
405        if( tr_bencDictFindList( args_in, "files-unwanted", &files ) )
406            setFileDLs( tor, FALSE, files );
407        if( tr_bencDictFindList( args_in, "files-wanted", &files ) )
408            setFileDLs( tor, TRUE, files );
409        if( tr_bencDictFindInt( args_in, "peer-limit", &tmp ) )
410            tr_torrentSetPeerLimit( tor, tmp );
411        if( tr_bencDictFindList( args_in, "priority-high", &files ) )
412            setFilePriorities( tor, TR_PRI_HIGH, files );
413        if( tr_bencDictFindList( args_in, "priority-low", &files ) )
414            setFilePriorities( tor, TR_PRI_LOW, files );
415        if( tr_bencDictFindList( args_in, "priority-normal", &files ) )
416            setFilePriorities( tor, TR_PRI_NORMAL, files );
417        if( tr_bencDictFindInt( args_in, "speed-limit-down", &tmp ) )
418            tr_torrentSetSpeedLimit( tor, TR_DOWN, tmp );
419        if( tr_bencDictFindInt( args_in, "speed-limit-down-enabled", &tmp ) )
420            tr_torrentSetSpeedMode( tor, TR_DOWN, tmp ? TR_SPEEDLIMIT_SINGLE
421                                                      : TR_SPEEDLIMIT_GLOBAL );
422        if( tr_bencDictFindInt( args_in, "speed-limit-up", &tmp ) )
423            tr_torrentSetSpeedLimit( tor, TR_UP, tmp );
424        if( tr_bencDictFindInt( args_in, "speed-limit-up-enabled", &tmp ) )
425            tr_torrentSetSpeedMode( tor, TR_UP, tmp ? TR_SPEEDLIMIT_SINGLE
426                                                    : TR_SPEEDLIMIT_GLOBAL );
427
428        notify( h, TR_RPC_TORRENT_CHANGED, tor );
429    }
430
431    tr_free( torrents );
432    return NULL;
433}
434
435/***
436****
437***/
438
439static const char*
440torrentAdd( tr_handle * h, tr_benc * args_in, tr_benc * args_out )
441{
442    const char * filename = NULL;
443    const char * metainfo_base64 = NULL;
444    tr_bencDictFindStr( args_in, "filename", &filename );
445    tr_bencDictFindStr( args_in, "metainfo", &metainfo_base64 );
446    if( !filename && !metainfo_base64 )
447        return "no filename or metainfo specified";
448    else
449    {
450        int64_t i;
451        int err = 0;
452        const char * str;
453        tr_ctor * ctor;
454        tr_torrent * tor;
455
456        ctor = tr_ctorNew( h );
457
458        /* set the metainfo */
459        if( filename )
460            tr_ctorSetMetainfoFromFile( ctor, filename );
461        else {
462            int len;
463            char * metainfo = tr_base64_decode( metainfo_base64, -1,  &len );
464            tr_ctorSetMetainfo( ctor, (uint8_t*)metainfo, len );
465            tr_free( metainfo );
466        }
467
468        /* set the optional arguments */
469        if( tr_bencDictFindStr( args_in, "download-dir", &str ) )
470            tr_ctorSetDownloadDir( ctor, TR_FORCE, str );
471        if( tr_bencDictFindInt( args_in, "paused", &i ) )
472            tr_ctorSetPaused( ctor, TR_FORCE, i );
473        if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
474            tr_ctorSetPeerLimit( ctor, TR_FORCE, i );
475
476        tor = tr_torrentNew( h, ctor, &err );
477        tr_ctorFree( ctor );
478
479        if( tor ) {
480            addInfo( tor, tr_bencDictAdd( args_out, "torrent-added" ), TR_RPC_TORRENT_ID );
481            notify( h, TR_RPC_TORRENT_ADDED, tor );
482        } else if( err == TR_EDUPLICATE ) {
483            return "duplicate torrent";
484        } else if( err == TR_EINVALID ) {
485            return "invalid or corrupt torrent file";
486        }
487    }
488
489    return NULL;
490}
491
492/***
493****
494***/
495
496static const char*
497sessionSet( tr_handle * h, tr_benc * args_in, tr_benc * args_out UNUSED )
498{
499    int64_t i;
500    const char * str;
501
502    if( tr_bencDictFindStr( args_in, "download-dir", &str ) )
503        tr_sessionSetDownloadDir( h, str );
504    if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
505        tr_sessionSetPeerLimit( h, i );
506    if( tr_bencDictFindInt( args_in, "pex-allowed", &i ) )
507        tr_sessionSetPexEnabled( h, i );
508    if( tr_bencDictFindInt( args_in, "port", &i ) )
509        tr_sessionSetPeerPort( h, i );
510    if( tr_bencDictFindInt( args_in, "port-forwarding-enabled", &i ) )
511        tr_sessionSetPortForwardingEnabled( h, i );
512    if( tr_bencDictFindInt( args_in, "speed-limit-down", &i ) )
513        tr_sessionSetSpeedLimit( h, TR_DOWN, i );
514    if( tr_bencDictFindInt( args_in, "speed-limit-down-enabled", &i ) )
515        tr_sessionSetSpeedLimitEnabled( h, TR_DOWN, i );
516    if( tr_bencDictFindInt( args_in, "speed-limit-up", &i ) )
517        tr_sessionSetSpeedLimit( h, TR_UP, i );
518    if( tr_bencDictFindInt( args_in, "speed-limit-up-enabled", &i ) )
519        tr_sessionSetSpeedLimitEnabled( h, TR_UP, i );
520    if( tr_bencDictFindStr( args_in, "encryption", &str ) ) {
521        if( !strcmp( str, "required" ) )
522            tr_sessionSetEncryption( h, TR_ENCRYPTION_REQUIRED );
523        else if( !strcmp( str, "tolerated" ) )
524            tr_sessionSetEncryption( h, TR_PLAINTEXT_PREFERRED );
525        else
526            tr_sessionSetEncryption( h, TR_ENCRYPTION_PREFERRED );
527    }
528
529    notify( h, TR_RPC_SESSION_CHANGED, NULL );
530
531    return NULL;
532}
533
534static const char*
535sessionStats( tr_handle * h, tr_benc * args_in UNUSED, tr_benc * args_out )
536{
537    tr_benc * d = tr_bencDictAddDict( args_out, "session-stats", 10 );
538    tr_torrent * tor = NULL;
539    float up, down;
540    int running = 0;
541    int total = 0;
542
543    tr_sessionGetSpeed( h, &down, &up );
544    while(( tor = tr_torrentNext( h, tor ))) {
545        ++total;
546        if( tor->isRunning )
547            ++running;
548    }
549       
550    tr_bencDictAddInt( d, "activeTorrentCount", running );
551    tr_bencDictAddInt( d, "downloadSpeed", (int)(down*1024) );
552    tr_bencDictAddInt( d, "pausedTorrentCount", total-running );
553    tr_bencDictAddInt( d, "torrentCount", total );
554    tr_bencDictAddInt( d, "uploadSpeed", (int)(up*1024) );
555    return NULL;
556}
557
558static const char*
559sessionGet( tr_handle * h, tr_benc * args_in UNUSED, tr_benc * args_out )
560{
561    const char * str;
562    tr_benc * d = args_out;
563
564    tr_bencDictAddStr( d, "download-dir",
565                          tr_sessionGetDownloadDir( h ) );
566    tr_bencDictAddInt( d, "peer-limit",
567                          tr_sessionGetPeerLimit( h ) );
568    tr_bencDictAddInt( d, "pex-allowed",
569                          tr_sessionIsPexEnabled( h ) );
570    tr_bencDictAddInt( d, "port",
571                          tr_sessionGetPeerPort( h ) );
572    tr_bencDictAddInt( d, "port-forwarding-enabled",
573                          tr_sessionIsPortForwardingEnabled( h ) );
574    tr_bencDictAddInt( d, "speed-limit-up",
575                          tr_sessionGetSpeedLimit( h, TR_UP ) * 1024 );
576    tr_bencDictAddInt( d, "speed-limit-up-enabled",
577                          tr_sessionIsSpeedLimitEnabled( h, TR_UP ) );
578    tr_bencDictAddInt( d, "speed-limit-down",
579                          tr_sessionGetSpeedLimit( h, TR_DOWN ) * 1024 );
580    tr_bencDictAddInt( d, "speed-limit-down-enabled",
581                          tr_sessionIsSpeedLimitEnabled( h, TR_DOWN ) );
582    switch( tr_sessionGetEncryption( h ) ) {
583        case TR_PLAINTEXT_PREFERRED: str = "tolerated"; break;
584        case TR_ENCRYPTION_REQUIRED: str = "required"; break;
585        default: str = "preferred"; break;
586    }
587    tr_bencDictAddStr( d, "encryption", str );
588
589    return NULL;
590}
591
592/***
593****
594***/
595
596typedef const char* (handler)( tr_handle*, tr_benc*, tr_benc* );
597
598struct method {
599    const char * name;
600    handler * func;
601} methods[] = { 
602    { "session-get", sessionGet },
603    { "session-set", sessionSet },
604    { "session-stats", sessionStats },
605    { "torrent-add", torrentAdd },
606    { "torrent-get", torrentGet },
607    { "torrent-remove", torrentRemove },
608    { "torrent-set", torrentSet },
609    { "torrent-start", torrentStart },
610    { "torrent-stop", torrentStop },
611    { "torrent-verify", torrentVerify }
612};
613
614static char*
615request_exec( struct tr_handle * handle,
616              tr_benc          * request,
617              int              * response_len )
618{
619    int64_t i;
620    const char * str;
621    char * out;
622    tr_benc response;
623    tr_benc * args_in = tr_bencDictFind( request, "arguments" );
624    tr_benc * args_out = NULL;
625    const char * result = NULL;
626
627    /* build the response skeleton */
628    tr_bencInitDict( &response, 3 );
629    args_out = tr_bencDictAddDict( &response, "arguments", 0 );
630
631    /* parse the request */
632    if( !tr_bencDictFindStr( request, "method", &str ) )
633        result = "no method name";
634    else {
635        const int n = TR_N_ELEMENTS( methods );
636        for( i=0; i<n; ++i )
637            if( !strcmp( str, methods[i].name ) )
638                break;
639        result = i==n
640            ? "method name not recognized"
641            : (*methods[i].func)( handle, args_in, args_out );
642    }
643
644    /* serialize & return the response */
645    if( !result )
646         result = "success";
647    tr_bencDictAddStr( &response, "result", result );
648    if( tr_bencDictFindInt( request, "tag", &i ) )
649        tr_bencDictAddInt( &response, "tag", i );
650    out = tr_bencSaveAsJSON( &response, response_len );
651    tr_bencFree( &response );
652    return out;
653}
654
655char*
656tr_rpc_request_exec_json( struct tr_handle  * handle,
657                          const void        * request_json,
658                          int                 request_len,
659                          int               * response_len )
660{
661    tr_benc top;
662    int have_content;
663    char * ret;
664
665    if( request_len < 0 )
666        request_len = strlen( request_json );
667
668    have_content = !tr_jsonParse( request_json, request_len, &top, NULL );
669    ret = request_exec( handle, have_content ? &top : NULL, response_len );
670
671    if( have_content )
672        tr_bencFree( &top );
673    return ret;
674}
675
676/**
677 * Munge the URI into a usable form.
678 *
679 * We have very loose typing on this to make the URIs as simple as possible:
680 * - anything not a 'tag' or 'method' is automatically in 'arguments'
681 * - values that are all-digits are numbers
682 * - values that are all-digits or commas are number lists
683 * - all other values are strings
684 */
685void
686tr_rpc_parse_list_str( tr_benc     * setme,
687                       const char  * str_in,
688                       size_t        len )
689
690{
691    char * str = tr_strndup( str_in, len );
692    int isNum;
693    int isNumList;
694    int commaCount;
695    const char * walk;
696
697    isNum = 1;
698    isNumList = 1;
699    commaCount = 0;
700    walk = str;
701    for( ; *walk && (isNumList || isNum); ++walk ) {
702        if( isNumList ) isNumList = *walk=='-' || isdigit(*walk) || *walk==',';
703        if( isNum     ) isNum     = *walk=='-' || isdigit(*walk);
704        if( *walk == ',' ) ++commaCount;
705    }
706
707    if( isNum )
708        tr_bencInitInt( setme, strtol( str, NULL, 10 ) );
709    else if( !isNumList )
710        tr_bencInitStrDup( setme, str );
711    else {
712        tr_bencInitList( setme, commaCount + 1 );
713        walk = str;
714        while( *walk ) {
715            char * p;
716            tr_bencListAddInt( setme, strtol( walk, &p, 10 ) );
717            if( *p!=',' )
718                break;
719            walk = p + 1;
720        }
721    }
722
723    tr_free( str );
724}
725
726
727char*
728tr_rpc_request_exec_uri( struct tr_handle  * handle,
729                         const void        * request_uri,
730                         int                 request_len,
731                         int               * response_len )
732{
733    char * ret = NULL;
734    tr_benc top, * args;
735    char * request = tr_strndup( request_uri, request_len );
736    const char * pch;
737
738    tr_bencInitDict( &top, 3 );
739    args = tr_bencDictAddDict( &top, "arguments", 0 );
740
741    pch = strchr( request, '?' );
742    if( !pch ) pch = request;
743    while( pch )
744    {
745        const char * delim = strchr( pch, '=' );
746        const char * next = strchr( pch, '&' );
747        if( delim )
748        {
749            char * key = tr_strndup( pch, delim-pch );
750            int isArg = strcmp( key, "method" ) && strcmp( key, "tag" );
751            tr_benc * parent = isArg ? args : &top;
752            tr_rpc_parse_list_str( tr_bencDictAdd( parent, key ),
753                                   delim+1,
754                                   next ? (size_t)(next-(delim+1)) : strlen(delim+1) );
755            tr_free( key );
756        }
757        pch = next ? next+1 : NULL;
758    }
759
760    ret = request_exec( handle, &top, response_len );
761
762    /* cleanup */
763    tr_bencFree( &top );
764    tr_free( request );
765    return ret;
766}
Note: See TracBrowser for help on using the repository browser.