source: branches/1.5x/libtransmission/rpcimpl.c @ 7733

Last change on this file since 7733 was 7733, checked in by charles, 12 years ago

(1.5x daemon) #1704: --info and/or --files should show the download directory

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