source: trunk/libtransmission/rpcimpl.c @ 7473

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

(trunk) #1029: When removing local data only remove data from the torrent

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