source: trunk/libtransmission/rpc.c @ 6293

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

spec fix: timesCompleted is an int, not a double.

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