source: trunk/libtransmission/rpc.c @ 6377

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

#1101 (rpc): add individual file progress to torrent-get's "files" message

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