source: trunk/libtransmission/rpc.c @ 6425

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

minor text cleanup

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