source: trunk/libtransmission/rpc.c @ 6345

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

(clutch) fix error with setting the incoming port via clutch preferences. reported by lolek in irc

  • 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 6345 2008-07-16 20:06:28Z 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_PEERS ) {
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.