source: trunk/libtransmission/rpc.c @ 5860

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

(1) RPC "add-torrent" now lets clients embed base64-encoded metainfo directly into the request
(2) remove the RISON code; it didn't make the final cut
(3) add base64 encode/decode utilities and unit tests

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