source: trunk/libtransmission/rpc.c @ 6210

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

(rpc) tweaks to the rpc spec.

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