source: trunk/libtransmission/rpc.c @ 6203

Last change on this file since 6203 was 6203, checked in by charles, 15 years ago

(RPC) add utility arguments for torrent-info: sort by (activity|age|id|name|progress|ratio|state|tracker), filter by (active|all|downloading|paused|seeding)

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