source: trunk/libtransmission/rpcimpl.c @ 7783

Last change on this file since 7783 was 7783, checked in by charles, 13 years ago

(trunk libT) fix some code that the newly-added gcc warnings didn't like

  • Property svn:keywords set to Date Rev Author Id
File size: 38.6 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 7783 2009-01-23 18:44:15Z 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 <event.h> /* evbuffer */
19
20#include "transmission.h"
21#include "bencode.h"
22#include "rpcimpl.h"
23#include "json.h"
24#include "session.h"
25#include "stats.h"
26#include "torrent.h"
27#include "utils.h"
28#include "web.h"
29
30#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) )
31
32#if 0
33#define dbgmsg(fmt, ...) \
34    do { \
35        fprintf( stderr, "%s:%d"#fmt, __FILE__, __LINE__, __VA_ARGS__ ); \
36        fprintf( stderr, "\n" ); \
37    } while( 0 )
38#else
39#define dbgmsg( ... ) \
40    do { \
41        if( tr_deepLoggingIsActive( ) ) \
42            tr_deepLog( __FILE__, __LINE__, "RPC", __VA_ARGS__ ); \
43    } while( 0 )
44#endif
45
46
47/***
48****
49***/
50
51static tr_rpc_callback_status
52notify( tr_session * session,
53        int          type,
54        tr_torrent * tor )
55{
56    tr_rpc_callback_status status = 0;
57
58    if( session->rpc_func )
59        status = session->rpc_func( session, type, tor,
60                                    session->rpc_func_user_data );
61
62    return status;
63}
64
65/***
66****
67***/
68
69/* For functions that can't be immediately executed, like torrentAdd,
70 * this is the callback data used to pass a response to the caller
71 * when the task is complete */
72struct tr_rpc_idle_data
73{
74    tr_session            * session;
75    tr_benc               * response;
76    tr_benc               * args_out;
77    tr_rpc_response_func    callback;
78    void                  * callback_user_data;
79};
80
81static void
82tr_idle_function_done( struct tr_rpc_idle_data * data, const char * result )
83{
84    struct evbuffer * buf = tr_getBuffer( );
85
86    if( result == NULL )
87        result = "success";
88    tr_bencDictAddStr( data->response, "result", result );
89
90    tr_bencSaveAsJSON( data->response, buf );
91    (*data->callback)( data->session, (const char*)EVBUFFER_DATA(buf),
92                       EVBUFFER_LENGTH(buf), data->callback_user_data );
93
94    tr_releaseBuffer( buf );
95    tr_bencFree( data->response );
96    tr_free( data->response );
97    tr_free( data );
98}
99
100/***
101****
102***/
103
104static tr_torrent **
105getTorrents( tr_session * session,
106             tr_benc    * args,
107             int        * setmeCount )
108{
109    int           torrentCount = 0;
110    int64_t       id;
111    tr_torrent ** torrents = NULL;
112    tr_benc *     ids;
113
114    if( tr_bencDictFindList( args, "ids", &ids ) )
115    {
116        int       i;
117        const int n = tr_bencListSize( ids );
118
119        torrents = tr_new0( tr_torrent *, n );
120
121        for( i = 0; i < n; ++i )
122        {
123            tr_torrent * tor = NULL;
124            tr_benc *    node = tr_bencListChild( ids, i );
125            const char * str;
126            if( tr_bencGetInt( node, &id ) )
127                tor = tr_torrentFindFromId( session, id );
128            else if( tr_bencGetStr( node, &str ) )
129                tor = tr_torrentFindFromHashString( session, str );
130            if( tor )
131                torrents[torrentCount++] = tor;
132        }
133    }
134    else if( tr_bencDictFindInt( args, "ids", &id )
135           || tr_bencDictFindInt( args, "id", &id ) )
136    {
137        tr_torrent * tor;
138        torrents = tr_new0( tr_torrent *, 1 );
139        if( ( tor = tr_torrentFindFromId( session, id ) ) )
140            torrents[torrentCount++] = tor;
141    }
142    else /* all of them */
143    {
144        tr_torrent * tor = NULL;
145        const int    n = tr_sessionCountTorrents( session );
146        torrents = tr_new0( tr_torrent *, n );
147        while( ( tor = tr_torrentNext( session, tor ) ) )
148            torrents[torrentCount++] = tor;
149    }
150
151    *setmeCount = torrentCount;
152    return torrents;
153}
154
155static const char*
156torrentStart( tr_session               * session,
157              tr_benc                  * args_in,
158              tr_benc                  * args_out UNUSED,
159              struct tr_rpc_idle_data  * idle_data )
160{
161    int           i, torrentCount;
162    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
163
164    assert( idle_data == NULL );
165
166    for( i = 0; i < torrentCount; ++i )
167    {
168        tr_torrent * tor = torrents[i];
169        tr_torrentStart( tor );
170        notify( session, TR_RPC_TORRENT_STARTED, tor );
171    }
172    tr_free( torrents );
173    return NULL;
174}
175
176static const char*
177torrentStop( tr_session               * session,
178             tr_benc                  * args_in,
179             tr_benc                  * args_out UNUSED,
180             struct tr_rpc_idle_data  * idle_data )
181{
182    int           i, torrentCount;
183    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
184
185    assert( idle_data == NULL );
186
187    for( i = 0; i < torrentCount; ++i )
188    {
189        tr_torrent * tor = torrents[i];
190        tr_torrentStop( tor );
191        notify( session, TR_RPC_TORRENT_STOPPED, tor );
192    }
193    tr_free( torrents );
194    return NULL;
195}
196
197static const char*
198torrentRemove( tr_session               * session,
199               tr_benc                  * args_in,
200               tr_benc                  * args_out UNUSED,
201               struct tr_rpc_idle_data  * idle_data )
202{
203    int i;
204    int torrentCount;
205    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
206
207    assert( idle_data == NULL );
208
209    for( i=0; i<torrentCount; ++i )
210    {
211        tr_torrent * tor = torrents[i];
212        const tr_rpc_callback_status status = notify( session, TR_RPC_TORRENT_REMOVING, tor );
213        int64_t deleteFlag;
214        if( tr_bencDictFindInt( args_in, "delete-local-data", &deleteFlag ) && deleteFlag )
215            tr_torrentDeleteLocalData( tor, NULL );
216        if( !( status & TR_RPC_NOREMOVE ) )
217            tr_torrentRemove( tor );
218    }
219
220    tr_free( torrents );
221    return NULL;
222}
223
224static const char*
225torrentVerify( tr_session               * session,
226               tr_benc                  * args_in,
227               tr_benc                  * args_out UNUSED,
228               struct tr_rpc_idle_data  * idle_data )
229{
230    int           i, torrentCount;
231    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
232
233    assert( idle_data == NULL );
234
235    for( i = 0; i < torrentCount; ++i )
236    {
237        tr_torrent * tor = torrents[i];
238        tr_torrentVerify( tor );
239        notify( session, TR_RPC_TORRENT_CHANGED, tor );
240    }
241
242    tr_free( torrents );
243    return NULL;
244}
245
246/***
247****
248***/
249
250static void
251addFiles( const tr_torrent * tor,
252          tr_benc *          list )
253{
254    tr_file_index_t i;
255    tr_file_index_t n;
256    const tr_info * info = tr_torrentInfo( tor );
257    tr_file_stat *  files = tr_torrentFiles( tor, &n );
258
259    for( i = 0; i < info->fileCount; ++i )
260    {
261        const tr_file * file = &info->files[i];
262        tr_benc *       d = tr_bencListAddDict( list, 3 );
263        tr_bencDictAddInt( d, "bytesCompleted", files[i].bytesCompleted );
264        tr_bencDictAddInt( d, "length", file->length );
265        tr_bencDictAddStr( d, "name", file->name );
266    }
267
268    tr_torrentFilesFree( files, n );
269}
270
271static void
272addWebseeds( const tr_info * info,
273             tr_benc *       webseeds )
274{
275    int i;
276
277    for( i = 0; i < info->webseedCount; ++i )
278        tr_bencListAddStr( webseeds, info->webseeds[i] );
279}
280
281static void
282addTrackers( const tr_info * info,
283             tr_benc *       trackers )
284{
285    int i;
286
287    for( i = 0; i < info->trackerCount; ++i )
288    {
289        const tr_tracker_info * t = &info->trackers[i];
290        tr_benc *               d = tr_bencListAddDict( trackers, 3 );
291        tr_bencDictAddStr( d, "announce", t->announce );
292        tr_bencDictAddStr( d, "scrape", t->scrape );
293        tr_bencDictAddInt( d, "tier", t->tier );
294    }
295}
296
297static void
298addPeers( const tr_torrent * tor,
299          tr_benc *          list )
300{
301    int            i;
302    int            peerCount;
303    tr_peer_stat * peers = tr_torrentPeers( tor, &peerCount );
304
305    tr_bencInitList( list, peerCount );
306
307    for( i = 0; i < peerCount; ++i )
308    {
309        tr_benc *            d = tr_bencListAddDict( list, 14 );
310        const tr_peer_stat * peer = peers + i;
311        tr_bencDictAddStr( d, "address", peer->addr );
312        tr_bencDictAddStr( d, "clientName", peer->client );
313        tr_bencDictAddInt( d, "clientIsChoked", peer->clientIsChoked );
314        tr_bencDictAddInt( d, "clientIsInterested",
315                           peer->clientIsInterested );
316        tr_bencDictAddStr( d, "flagStr", peer->flagStr );
317        tr_bencDictAddInt( d, "isDownloadingFrom", peer->isDownloadingFrom );
318        tr_bencDictAddInt( d, "isEncrypted", peer->isEncrypted );
319        tr_bencDictAddInt( d, "isIncoming", peer->isIncoming );
320        tr_bencDictAddInt( d, "isUploadingTo", peer->isUploadingTo );
321        tr_bencDictAddInt( d, "peerIsChoked", peer->peerIsChoked );
322        tr_bencDictAddInt( d, "peerIsInterested", peer->peerIsInterested );
323        tr_bencDictAddInt( d, "port", peer->port );
324        tr_bencDictAddDouble( d, "progress", peer->progress );
325        tr_bencDictAddInt( d, "rateToClient",
326                          (int)( peer->rateToClient * 1024.0 ) );
327        tr_bencDictAddInt( d, "rateToPeer",
328                          (int)( peer->rateToPeer * 1024.0 ) );
329    }
330
331    tr_torrentPeersFree( peers, peerCount );
332}
333
334static void
335addField( const tr_torrent * tor,
336          tr_benc *          d,
337          const char *       key )
338{
339    const tr_info * inf = tr_torrentInfo( tor );
340    const tr_stat * st = tr_torrentStat( (tr_torrent*)tor );
341
342    if( !strcmp( key, "activityDate" ) )
343        tr_bencDictAddInt( d, key, st->activityDate );
344    else if( !strcmp( key, "addedDate" ) )
345        tr_bencDictAddInt( d, key, st->addedDate );
346    else if( !strcmp( key, "announceResponse" ) )
347        tr_bencDictAddStr( d, key, st->announceResponse );
348    else if( !strcmp( key, "announceURL" ) )
349        tr_bencDictAddStr( d, key, st->announceURL );
350    else if( !strcmp( key, "comment" ) )
351        tr_bencDictAddStr( d, key, inf->comment ? inf->comment : "" );
352    else if( !strcmp( key, "corruptEver" ) )
353        tr_bencDictAddInt( d, key, st->corruptEver );
354    else if( !strcmp( key, "creator" ) )
355        tr_bencDictAddStr( d, key, inf->creator ? inf->creator : "" );
356    else if( !strcmp( key, "dateCreated" ) )
357        tr_bencDictAddInt( d, key, inf->dateCreated );
358    else if( !strcmp( key, "desiredAvailable" ) )
359        tr_bencDictAddInt( d, key, st->desiredAvailable );
360    else if( !strcmp( key, "doneDate" ) )
361        tr_bencDictAddInt( d, key, st->doneDate );
362    else if( !strcmp( key, "downloadDir" ) )
363        tr_bencDictAddStr( d, key, tr_torrentGetDownloadDir( tor ) );
364    else if( !strcmp( key, "downloadedEver" ) )
365        tr_bencDictAddInt( d, key, st->downloadedEver );
366    else if( !strcmp( key, "downloaders" ) )
367        tr_bencDictAddInt( d, key, st->downloaders );
368    else if( !strcmp( key, "downloadLimitMode" ) )
369        tr_bencDictAddInt( d, key, tr_torrentGetSpeedMode( tor, TR_DOWN ) );
370    else if( !strcmp( key, "downloadLimit" ) )
371        tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit( tor, TR_DOWN ) );
372    else if( !strcmp( key, "error" ) )
373        tr_bencDictAddInt( d, key, st->error );
374    else if( !strcmp( key, "errorString" ) )
375        tr_bencDictAddStr( d, key, st->errorString );
376    else if( !strcmp( key, "eta" ) )
377        tr_bencDictAddInt( d, key, st->eta );
378    else if( !strcmp( key, "files" ) )
379        addFiles( tor, tr_bencDictAddList( d, key, inf->fileCount ) );
380    else if( !strcmp( key, "hashString" ) )
381        tr_bencDictAddStr( d, key, tor->info.hashString );
382    else if( !strcmp( key, "haveUnchecked" ) )
383        tr_bencDictAddInt( d, key, st->haveUnchecked );
384    else if( !strcmp( key, "haveValid" ) )
385        tr_bencDictAddInt( d, key, st->haveValid );
386    else if( !strcmp( key, "id" ) )
387        tr_bencDictAddInt( d, key, st->id );
388    else if( !strcmp( key, "isPrivate" ) )
389        tr_bencDictAddInt( d, key, tr_torrentIsPrivate( tor ) );
390    else if( !strcmp( key, "lastAnnounceTime" ) )
391        tr_bencDictAddInt( d, key, st->lastAnnounceTime );
392    else if( !strcmp( key, "lastScrapeTime" ) )
393        tr_bencDictAddInt( d, key, st->lastScrapeTime );
394    else if( !strcmp( key, "leechers" ) )
395        tr_bencDictAddInt( d, key, st->leechers );
396    else if( !strcmp( key, "leftUntilDone" ) )
397        tr_bencDictAddInt( d, key, st->leftUntilDone );
398    else if( !strcmp( key, "manualAnnounceTime" ) )
399        tr_bencDictAddInt( d, key, st->manualAnnounceTime );
400    else if( !strcmp( key, "maxConnectedPeers" ) )
401        tr_bencDictAddInt( d, key,  tr_torrentGetPeerLimit( tor ) );
402    else if( !strcmp( key, "name" ) )
403        tr_bencDictAddStr( d, key, inf->name );
404    else if( !strcmp( key, "nextAnnounceTime" ) )
405        tr_bencDictAddInt( d, key, st->nextAnnounceTime );
406    else if( !strcmp( key, "nextScrapeTime" ) )
407        tr_bencDictAddInt( d, key, st->nextScrapeTime );
408    else if( !strcmp( key, "peers" ) )
409        addPeers( tor, tr_bencDictAdd( d, key ) );
410    else if( !strcmp( key, "peersConnected" ) )
411        tr_bencDictAddInt( d, key, st->peersConnected );
412    else if( !strcmp( key, "peersFrom" ) )
413    {
414        tr_benc *   tmp = tr_bencDictAddDict( d, key, 4 );
415        const int * f = st->peersFrom;
416        tr_bencDictAddInt( tmp, "fromCache",    f[TR_PEER_FROM_CACHE] );
417        tr_bencDictAddInt( tmp, "fromIncoming", f[TR_PEER_FROM_INCOMING] );
418        tr_bencDictAddInt( tmp, "fromPex",      f[TR_PEER_FROM_PEX] );
419        tr_bencDictAddInt( tmp, "fromTracker",  f[TR_PEER_FROM_TRACKER] );
420    }
421    else if( !strcmp( key, "peersGettingFromUs" ) )
422        tr_bencDictAddInt( d, key, st->peersGettingFromUs );
423    else if( !strcmp( key, "peersKnown" ) )
424        tr_bencDictAddInt( d, key, st->peersKnown );
425    else if( !strcmp( key, "peersSendingToUs" ) )
426        tr_bencDictAddInt( d, key, st->peersSendingToUs );
427    else if( !strcmp( key, "pieceCount" ) )
428        tr_bencDictAddInt( d, key, inf->pieceCount );
429    else if( !strcmp( key, "pieceSize" ) )
430        tr_bencDictAddInt( d, key, inf->pieceSize );
431    else if( !strcmp( key, "priorities" ) )
432    {
433        tr_file_index_t i;
434        tr_benc *       p = tr_bencDictAddList( d, key, inf->fileCount );
435        for( i = 0; i < inf->fileCount; ++i )
436            tr_bencListAddInt( p, inf->files[i].priority );
437    }
438    else if( !strcmp( key, "rateDownload" ) )
439        tr_bencDictAddInt( d, key, (int)( st->pieceDownloadSpeed * 1024 ) );
440    else if( !strcmp( key, "rateUpload" ) )
441        tr_bencDictAddInt( d, key, (int)( st->pieceUploadSpeed * 1024 ) );
442    else if( !strcmp( key, "recheckProgress" ) )
443        tr_bencDictAddDouble( d, key, st->recheckProgress );
444    else if( !strcmp( key, "scrapeResponse" ) )
445        tr_bencDictAddStr( d, key, st->scrapeResponse );
446    else if( !strcmp( key, "scrapeURL" ) )
447        tr_bencDictAddStr( d, key, st->scrapeURL );
448    else if( !strcmp( key, "seeders" ) )
449        tr_bencDictAddInt( d, key, st->seeders );
450    else if( !strcmp( key, "sizeWhenDone" ) )
451        tr_bencDictAddInt( d, key, st->sizeWhenDone );
452    else if( !strcmp( key, "startDate" ) )
453        tr_bencDictAddInt( d, key, st->startDate );
454    else if( !strcmp( key, "status" ) )
455        tr_bencDictAddInt( d, key, st->activity );
456    else if( !strcmp( key, "swarmSpeed" ) )
457        tr_bencDictAddInt( d, key, (int)( st->swarmSpeed * 1024 ) );
458    else if( !strcmp( key, "timesCompleted" ) )
459        tr_bencDictAddInt( d, key, st->timesCompleted );
460    else if( !strcmp( key, "trackers" ) )
461        addTrackers( inf, tr_bencDictAddList( d, key, inf->trackerCount ) );
462    else if( !strcmp( key, "totalSize" ) )
463        tr_bencDictAddInt( d, key, inf->totalSize );
464    else if( !strcmp( key, "uploadedEver" ) )
465        tr_bencDictAddInt( d, key, st->uploadedEver );
466    else if( !strcmp( key, "uploadLimitMode" ) )
467        tr_bencDictAddInt( d, key,   tr_torrentGetSpeedMode( tor, TR_UP ) );
468    else if( !strcmp( key, "uploadLimit" ) )
469        tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit( tor, TR_UP ) );
470    else if( !strcmp( key, "uploadRatio" ) )
471        tr_bencDictAddDouble( d, key,
472                             tr_getRatio( st->uploadedEver,
473                                          st->downloadedEver ) );
474    else if( !strcmp( key, "wanted" ) )
475    {
476        tr_file_index_t i;
477        tr_benc *       w = tr_bencDictAddList( d, key, inf->fileCount );
478        for( i = 0; i < inf->fileCount; ++i )
479            tr_bencListAddInt( w, inf->files[i].dnd ? 0 : 1 );
480    }
481    else if( !strcmp( key, "webseeds" ) )
482        addWebseeds( inf, tr_bencDictAddList( d, key, inf->trackerCount ) );
483    else if( !strcmp( key, "webseedsSendingToUs" ) )
484        tr_bencDictAddInt( d, key, st->webseedsSendingToUs );
485}
486
487static void
488addInfo( const tr_torrent * tor,
489         tr_benc *          d,
490         tr_benc *          fields )
491{
492    int          i;
493    const int    n = tr_bencListSize( fields );
494    const char * str;
495
496    tr_bencInitDict( d, n );
497
498    for( i = 0; i < n; ++i )
499        if( tr_bencGetStr( tr_bencListChild( fields, i ), &str ) )
500            addField( tor, d, str );
501}
502
503static const char*
504torrentGet( tr_session               * session,
505            tr_benc                  * args_in,
506            tr_benc                  * args_out,
507            struct tr_rpc_idle_data  * idle_data )
508{
509    int           i, torrentCount;
510    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
511    tr_benc *     list = tr_bencDictAddList( args_out, "torrents", torrentCount );
512    tr_benc *     fields;
513    const char *  msg = NULL;
514
515    assert( idle_data == NULL );
516
517    if( !tr_bencDictFindList( args_in, "fields", &fields ) )
518        msg = "no fields specified";
519    else for( i = 0; i < torrentCount; ++i )
520            addInfo( torrents[i], tr_bencListAdd( list ), fields );
521
522    tr_free( torrents );
523    return msg;
524}
525
526/***
527****
528***/
529
530static void
531setFilePriorities( tr_torrent * tor,
532                   int          priority,
533                   tr_benc *    list )
534{
535    int               i;
536    int64_t           tmp;
537    int               fileCount = 0;
538    const int         n = tr_bencListSize( list );
539    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
540
541    if( n )
542    {
543        for( i = 0; i < n; ++i )
544            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) )
545                if( 0 <= tmp && tmp < tor->info.fileCount )
546                    files[fileCount++] = tmp;
547    }
548    else /* if empty set, apply to all */
549    {
550        tr_file_index_t t;
551        for( t = 0; t < tor->info.fileCount; ++t )
552            files[fileCount++] = t;
553    }
554
555    if( fileCount )
556        tr_torrentSetFilePriorities( tor, files, fileCount, priority );
557
558    tr_free( files );
559}
560
561static void
562setFileDLs( tr_torrent * tor,
563            int          do_download,
564            tr_benc *    list )
565{
566    int               i;
567    int64_t           tmp;
568    int               fileCount = 0;
569    const int         n = tr_bencListSize( list );
570    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
571
572    if( n ) /* if argument list, process them */
573    {
574        for( i = 0; i < n; ++i )
575            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) )
576                if( 0 <= tmp && tmp < tor->info.fileCount )
577                    files[fileCount++] = tmp;
578    }
579    else /* if empty set, apply to all */
580    {
581        tr_file_index_t t;
582        for( t = 0; t < tor->info.fileCount; ++t )
583            files[fileCount++] = t;
584    }
585
586    if( fileCount )
587        tr_torrentSetFileDLs( tor, files, fileCount, do_download );
588
589    tr_free( files );
590}
591
592static const char*
593torrentSet( tr_session               * session,
594            tr_benc                  * args_in,
595            tr_benc                  * args_out UNUSED,
596            struct tr_rpc_idle_data  * idle_data )
597{
598    int           i, torrentCount;
599    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
600
601    assert( idle_data == NULL );
602
603    for( i = 0; i < torrentCount; ++i )
604    {
605        int64_t      tmp;
606        tr_benc *    files;
607        tr_torrent * tor = torrents[i];
608
609        if( tr_bencDictFindList( args_in, "files-unwanted", &files ) )
610            setFileDLs( tor, FALSE, files );
611        if( tr_bencDictFindList( args_in, "files-wanted", &files ) )
612            setFileDLs( tor, TRUE, files );
613        if( tr_bencDictFindInt( args_in, "peer-limit", &tmp ) )
614            tr_torrentSetPeerLimit( tor, tmp );
615        if( tr_bencDictFindList( args_in, "priority-high", &files ) )
616            setFilePriorities( tor, TR_PRI_HIGH, files );
617        if( tr_bencDictFindList( args_in, "priority-low", &files ) )
618            setFilePriorities( tor, TR_PRI_LOW, files );
619        if( tr_bencDictFindList( args_in, "priority-normal", &files ) )
620            setFilePriorities( tor, TR_PRI_NORMAL, files );
621        if( tr_bencDictFindInt( args_in, "speed-limit-down", &tmp ) )
622            tr_torrentSetSpeedLimit( tor, TR_DOWN, tmp );
623        if( tr_bencDictFindInt( args_in, "speed-limit-down-enabled", &tmp ) )
624            tr_torrentSetSpeedMode(
625                tor, TR_DOWN, tmp ? TR_SPEEDLIMIT_SINGLE
626                : TR_SPEEDLIMIT_GLOBAL );
627        if( tr_bencDictFindInt( args_in, "speed-limit-up", &tmp ) )
628            tr_torrentSetSpeedLimit( tor, TR_UP, tmp );
629        if( tr_bencDictFindInt( args_in, "speed-limit-up-enabled", &tmp ) )
630            tr_torrentSetSpeedMode( tor, TR_UP, tmp ? TR_SPEEDLIMIT_SINGLE
631                                    : TR_SPEEDLIMIT_GLOBAL );
632
633        notify( session, TR_RPC_TORRENT_CHANGED, tor );
634    }
635
636    tr_free( torrents );
637    return NULL;
638}
639
640/***
641****
642***/
643
644static void
645addTorrentImpl( struct tr_rpc_idle_data * data, tr_ctor * ctor )
646{
647    int err = 0;
648    const char * result = NULL;
649    tr_torrent * tor = tr_torrentNew( data->session, ctor, &err );
650
651    tr_ctorFree( ctor );
652
653    if( tor )
654    {
655        tr_benc fields;
656        tr_bencInitList( &fields, 3 );
657        tr_bencListAddStr( &fields, "id" );
658        tr_bencListAddStr( &fields, "name" );
659        tr_bencListAddStr( &fields, "hashString" );
660        addInfo( tor, tr_bencDictAdd( data->args_out, "torrent-added" ), &fields );
661        notify( data->session, TR_RPC_TORRENT_ADDED, tor );
662        tr_bencFree( &fields );
663    }
664    else if( err == TR_EDUPLICATE )
665    {
666        result = "duplicate torrent";
667    }
668    else if( err == TR_EINVALID )
669    {
670        result = "invalid or corrupt torrent file";
671    }
672
673    tr_idle_function_done( data, result );
674}
675
676
677struct add_torrent_idle_data
678{
679    struct tr_rpc_idle_data * data;
680    tr_ctor * ctor;
681};
682
683static void
684gotMetadataFromURL( tr_session       * session UNUSED,
685                    long               response_code,
686                    const void       * response,
687                    size_t             response_byte_count,
688                    void             * user_data )
689{
690    struct add_torrent_idle_data * data = user_data;
691
692    dbgmsg( "torrentAdd: HTTP response code was %ld (%s); response length was %zu bytes",
693            response_code, tr_webGetResponseStr( response_code ), response_byte_count );
694
695    if( response_code == 200 )
696    {
697        tr_ctorSetMetainfo( data->ctor, response, response_byte_count );
698        addTorrentImpl( data->data, data->ctor );
699    }
700    else
701    {
702        char result[1024];
703        tr_snprintf( result, sizeof( result ), "http error %ld: %s",
704                     response_code, tr_webGetResponseStr( response_code ) );
705        tr_idle_function_done( data->data, result );
706    }
707
708    tr_free( data );
709}
710
711static tr_bool
712isCurlURL( const char * filename )
713{
714    if( filename == NULL )
715        return FALSE;
716
717    return ( strstr( filename, "ftp://" ) != NULL )
718        || ( strstr( filename, "http://" ) != NULL )
719        || ( strstr( filename, "https://" ) != NULL );
720}
721
722static const char*
723torrentAdd( tr_session               * session,
724            tr_benc                  * args_in,
725            tr_benc                  * args_out UNUSED,
726            struct tr_rpc_idle_data  * idle_data )
727{
728    const char * filename = NULL;
729    const char * metainfo_base64 = NULL;
730
731    assert( idle_data != NULL );
732
733    tr_bencDictFindStr( args_in, "filename", &filename );
734    tr_bencDictFindStr( args_in, "metainfo", &metainfo_base64 );
735    if( !filename && !metainfo_base64 )
736        return "no filename or metainfo specified";
737    else
738    {
739        int64_t      i;
740        const char * str;
741        tr_ctor    * ctor = tr_ctorNew( session );
742
743        /* set the optional arguments */
744        if( tr_bencDictFindStr( args_in, "download-dir", &str ) )
745            tr_ctorSetDownloadDir( ctor, TR_FORCE, str );
746        if( tr_bencDictFindInt( args_in, "paused", &i ) )
747            tr_ctorSetPaused( ctor, TR_FORCE, i );
748        if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
749            tr_ctorSetPeerLimit( ctor, TR_FORCE, i );
750
751        dbgmsg( "torrentAdd: filename is \"%s\"", filename );
752
753        if( isCurlURL( filename ) )
754        {
755            struct add_torrent_idle_data * d = tr_new0( struct add_torrent_idle_data, 1 );
756            d->data = idle_data;
757            d->ctor = ctor;
758            tr_webRun( session, filename, NULL, gotMetadataFromURL, d );
759        }
760        else
761        {
762            if( filename != NULL )
763                tr_ctorSetMetainfoFromFile( ctor, filename );
764            else {
765                int len;
766                char * metainfo = tr_base64_decode( metainfo_base64, -1,  &len );
767                tr_ctorSetMetainfo( ctor, (uint8_t*)metainfo, len );
768                tr_free( metainfo );
769            }
770            addTorrentImpl( idle_data, ctor );
771        }
772
773    }
774
775    return NULL;
776}
777
778/***
779****
780***/
781
782static const char*
783sessionSet( tr_session               * session,
784            tr_benc                  * args_in,
785            tr_benc                  * args_out UNUSED,
786            struct tr_rpc_idle_data  * idle_data )
787{
788    int64_t      i;
789    const char * str;
790
791    assert( idle_data == NULL );
792
793    if( tr_bencDictFindStr( args_in, "download-dir", &str ) )
794        tr_sessionSetDownloadDir( session, str );
795    if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
796        tr_sessionSetPeerLimit( session, i );
797    if( tr_bencDictFindInt( args_in, "pex-allowed", &i ) )
798        tr_sessionSetPexEnabled( session, i );
799    if( tr_bencDictFindInt( args_in, "port", &i ) )
800        tr_sessionSetPeerPort( session, i );
801    if( tr_bencDictFindInt( args_in, "port-forwarding-enabled", &i ) )
802        tr_sessionSetPortForwardingEnabled( session, i );
803    if( tr_bencDictFindInt( args_in, "speed-limit-down", &i ) )
804        tr_sessionSetSpeedLimit( session, TR_DOWN, i );
805    if( tr_bencDictFindInt( args_in, "speed-limit-down-enabled", &i ) )
806        tr_sessionSetSpeedLimitEnabled( session, TR_DOWN, i );
807    if( tr_bencDictFindInt( args_in, "speed-limit-up", &i ) )
808        tr_sessionSetSpeedLimit( session, TR_UP, i );
809    if( tr_bencDictFindInt( args_in, "speed-limit-up-enabled", &i ) )
810        tr_sessionSetSpeedLimitEnabled( session, TR_UP, i );
811    if( tr_bencDictFindStr( args_in, "encryption", &str ) )
812    {
813        if( !strcmp( str, "required" ) )
814            tr_sessionSetEncryption( session, TR_ENCRYPTION_REQUIRED );
815        else if( !strcmp( str, "tolerated" ) )
816            tr_sessionSetEncryption( session, TR_CLEAR_PREFERRED );
817        else
818            tr_sessionSetEncryption( session, TR_ENCRYPTION_PREFERRED );
819    }
820
821    notify( session, TR_RPC_SESSION_CHANGED, NULL );
822
823    return NULL;
824}
825
826static const char*
827sessionStats( tr_session               * session,
828              tr_benc                  * args_in UNUSED,
829              tr_benc                  * args_out,
830              struct tr_rpc_idle_data  * idle_data )
831{
832    int running = 0;
833    int total = 0;
834    tr_benc * d; 
835    tr_session_stats currentStats = { 0.0f, 0, 0, 0, 0, 0 }; 
836    tr_session_stats cumulativeStats = { 0.0f, 0, 0, 0, 0, 0 }; 
837    tr_torrent * tor = NULL;
838
839    assert( idle_data == NULL );
840
841    while(( tor = tr_torrentNext( session, tor ))) {
842        ++total;
843        if( tor->isRunning )
844            ++running;
845    }
846
847    tr_sessionGetStats( session, &currentStats ); 
848    tr_sessionGetCumulativeStats( session, &cumulativeStats ); 
849
850    tr_bencDictAddInt( args_out, "activeTorrentCount", running );
851    tr_bencDictAddInt( args_out, "downloadSpeed", (int)( tr_sessionGetPieceSpeed( session, TR_DOWN ) * 1024 ) );
852    tr_bencDictAddInt( args_out, "pausedTorrentCount", total - running );
853    tr_bencDictAddInt( args_out, "torrentCount", total );
854    tr_bencDictAddInt( args_out, "uploadSpeed", (int)( tr_sessionGetPieceSpeed( session, TR_UP ) * 1024 ) );
855
856    d = tr_bencDictAddDict( args_out, "cumulative-stats", 5 ); 
857    tr_bencDictAddInt( d, "downloadedBytes", cumulativeStats.downloadedBytes ); 
858    tr_bencDictAddInt( d, "filesAdded", cumulativeStats.filesAdded ); 
859    tr_bencDictAddInt( d, "secondsActive", cumulativeStats.secondsActive ); 
860    tr_bencDictAddInt( d, "sessionCount", cumulativeStats.sessionCount ); 
861    tr_bencDictAddInt( d, "uploadedBytes", cumulativeStats.uploadedBytes ); 
862
863    d = tr_bencDictAddDict( args_out, "current-stats", 5 ); 
864    tr_bencDictAddInt( d, "downloadedBytes", currentStats.downloadedBytes ); 
865    tr_bencDictAddInt( d, "filesAdded", currentStats.filesAdded ); 
866    tr_bencDictAddInt( d, "secondsActive", currentStats.secondsActive ); 
867    tr_bencDictAddInt( d, "sessionCount", currentStats.sessionCount ); 
868    tr_bencDictAddInt( d, "uploadedBytes", currentStats.uploadedBytes ); 
869
870    return NULL;
871}
872
873static const char*
874sessionGet( tr_session               * session,
875            tr_benc                  * args_in UNUSED,
876            tr_benc                  * args_out,
877            struct tr_rpc_idle_data  * idle_data )
878{
879    const char * str;
880    tr_benc *    d = args_out;
881
882    assert( idle_data == NULL );
883
884    tr_bencDictAddStr( d, "download-dir", tr_sessionGetDownloadDir( session ) );
885    tr_bencDictAddInt( d, "peer-limit", tr_sessionGetPeerLimit( session ) );
886    tr_bencDictAddInt( d, "pex-allowed", tr_sessionIsPexEnabled( session ) );
887    tr_bencDictAddInt( d, "port", tr_sessionGetPeerPort( session ) );
888    tr_bencDictAddInt( d, "port-forwarding-enabled", tr_sessionIsPortForwardingEnabled( session ) );
889    tr_bencDictAddInt( d, "rpc-version", 4 );
890    tr_bencDictAddInt( d, "rpc-version-minimum", 1 );
891    tr_bencDictAddInt( d, "speed-limit-up", tr_sessionGetSpeedLimit( session, TR_UP ) );
892    tr_bencDictAddInt( d, "speed-limit-up-enabled", tr_sessionIsSpeedLimitEnabled( session, TR_UP ) );
893    tr_bencDictAddInt( d, "speed-limit-down", tr_sessionGetSpeedLimit( session, TR_DOWN ) );
894    tr_bencDictAddInt( d, "speed-limit-down-enabled", tr_sessionIsSpeedLimitEnabled( session, TR_DOWN ) );
895    tr_bencDictAddStr( d, "version", LONG_VERSION_STRING );
896    switch( tr_sessionGetEncryption( session ) ) {
897        case TR_CLEAR_PREFERRED: str = "tolerated"; break;
898        case TR_ENCRYPTION_REQUIRED: str = "required"; break; 
899        default: str = "preferred"; break;
900    }
901    tr_bencDictAddStr( d, "encryption", str );
902
903    return NULL;
904}
905
906/***
907****
908***/
909
910typedef const char* ( *handler )( tr_session*, tr_benc*, tr_benc*, struct tr_rpc_idle_data * );
911
912static struct method
913{
914    const char *  name;
915    tr_bool       immediate;
916    handler       func;
917}
918methods[] =
919{
920    { "session-get",    TRUE,  sessionGet          },
921    { "session-set",    TRUE,  sessionSet          },
922    { "session-stats",  TRUE,  sessionStats        },
923    { "torrent-add",    FALSE, torrentAdd          },
924    { "torrent-get",    TRUE,  torrentGet          },
925    { "torrent-remove", TRUE,  torrentRemove       },
926    { "torrent-set",    TRUE,  torrentSet          },
927    { "torrent-start",  TRUE,  torrentStart        },
928    { "torrent-stop",   TRUE,  torrentStop         },
929    { "torrent-verify", TRUE,  torrentVerify       }
930};
931
932static void
933noop_response_callback( tr_session * session UNUSED,
934                        const char * response UNUSED,
935                        size_t       response_len UNUSED,
936                        void       * user_data UNUSED )
937{
938}
939
940static void
941request_exec( tr_session             * session,
942              tr_benc                * request,
943              tr_rpc_response_func     callback,
944              void                   * callback_user_data )
945{
946    int i;
947    const char * str;
948    tr_benc * args_in = tr_bencDictFind( request, "arguments" );
949    const char * result = NULL;
950
951    if( callback == NULL )
952        callback = noop_response_callback;
953
954    /* parse the request */
955    if( !tr_bencDictFindStr( request, "method", &str ) )
956        result = "no method name";
957    else {
958        const int n = TR_N_ELEMENTS( methods );
959        for( i = 0; i < n; ++i )
960            if( !strcmp( str, methods[i].name ) )
961                break;
962        if( i ==n )
963            result = "method name not recognized";
964    }
965
966    /* if we couldn't figure out which method to use, return an error */
967    if( result != NULL )
968    {
969        int64_t tag;
970        tr_benc response;
971        struct evbuffer * buf = tr_getBuffer( );
972
973        tr_bencInitDict( &response, 3 );
974        tr_bencDictAddDict( &response, "arguments", 0 );
975        tr_bencDictAddStr( &response, "result", result );
976        if( tr_bencDictFindInt( request, "tag", &tag ) )
977            tr_bencDictAddInt( &response, "tag", tag );
978        tr_bencSaveAsJSON( &response, buf );
979        (*callback)( session, (const char*)EVBUFFER_DATA(buf),
980                     EVBUFFER_LENGTH( buf ), callback_user_data );
981
982        tr_releaseBuffer( buf );
983        tr_bencFree( &response );
984    }
985
986    if( methods[i].immediate )
987    {
988        int64_t tag;
989        tr_benc response;
990        tr_benc * args_out;
991        struct evbuffer * buf = tr_getBuffer( );
992
993        tr_bencInitDict( &response, 3 );
994        args_out = tr_bencDictAddDict( &response, "arguments", 0 );
995        result = (*methods[i].func)( session, args_in, args_out, NULL );
996        if( result == NULL )
997            result = "success";
998        tr_bencDictAddStr( &response, "result", result );
999        if( tr_bencDictFindInt( request, "tag", &tag ) )
1000            tr_bencDictAddInt( &response, "tag", tag );
1001        tr_bencSaveAsJSON( &response, buf );
1002        (*callback)( session, (const char*)EVBUFFER_DATA(buf),
1003                     EVBUFFER_LENGTH(buf), callback_user_data );
1004
1005        tr_releaseBuffer( buf );
1006        tr_bencFree( &response );
1007    }
1008    else
1009    {
1010        int64_t tag;
1011        struct tr_rpc_idle_data * data = tr_new0( struct tr_rpc_idle_data, 1 );
1012        data->session = session;
1013        data->response = tr_new0( tr_benc, 1 );
1014        tr_bencInitDict( data->response, 3 );
1015        if( tr_bencDictFindInt( request, "tag", &tag ) )
1016            tr_bencDictAddInt( data->response, "tag", tag );
1017        data->args_out = tr_bencDictAddDict( data->response, "arguments", 0 );
1018        data->callback = callback;
1019        data->callback_user_data = callback_user_data;
1020        (*methods[i].func)( session, args_in, data->args_out, data );
1021    }
1022}
1023
1024void
1025tr_rpc_request_exec_json( tr_session            * session,
1026                          const void            * request_json,
1027                          int                     request_len,
1028                          tr_rpc_response_func    callback,
1029                          void                  * callback_user_data )
1030{
1031    tr_benc top;
1032    int have_content;
1033
1034    if( request_len < 0 )
1035        request_len = strlen( request_json );
1036
1037    have_content = !tr_jsonParse( request_json, request_len, &top, NULL );
1038    request_exec( session, have_content ? &top : NULL, callback, callback_user_data );
1039
1040    if( have_content )
1041        tr_bencFree( &top );
1042}
1043
1044static void
1045addToken( tr_benc *    list,
1046          const char * token,
1047          size_t       len )
1048{
1049    char *       p;
1050    const char * end = token + len;
1051    const long   a = strtol( token, &p, 10 );
1052
1053    if( p == end )
1054        tr_bencListAddInt( list, a );
1055    else if( *p == '-' && isdigit( p[1] ) )
1056    {
1057        const long b = strtol( p + 1, &p, 10 );
1058        if( ( p == end ) && ( b > a ) )
1059        {
1060            long i;
1061            for( i = a; i <= b; ++i )
1062                tr_bencListAddInt( list, i );
1063        }
1064    }
1065}
1066
1067/**
1068 * Munge the URI into a usable form.
1069 *
1070 * We have very loose typing on this to make the URIs as simple as possible:
1071 * - anything not a 'tag' or 'method' is automatically in 'arguments'
1072 * - values that are all-digits are numbers
1073 * - values that are all-digits or commas are number lists
1074 * - all other values are strings
1075 */
1076void
1077tr_rpc_parse_list_str( tr_benc     * setme,
1078                       const char  * str_in,
1079                       int           len )
1080
1081{
1082    char *       str = tr_strndup( str_in, len );
1083    int          isNum;
1084    int          isNumList;
1085    int          commaCount;
1086    const char * walk;
1087
1088    isNum = 1;
1089    isNumList = 1;
1090    commaCount = 0;
1091    walk = str;
1092    for( ; *walk && ( isNumList || isNum ); ++walk )
1093    {
1094        if( isNumList ) isNumList = *walk == '-' || isdigit( *walk )
1095                                    || *walk == ',';
1096        if( isNum     ) isNum     = *walk == '-' || isdigit( *walk );
1097        if( *walk == ',' ) ++commaCount;
1098    }
1099
1100    if( isNum )
1101        tr_bencInitInt( setme, strtol( str, NULL, 10 ) );
1102    else if( !isNumList )
1103        tr_bencInitStr( setme, str, len );
1104    else
1105    {
1106        tr_bencInitList( setme, commaCount + 1 );
1107        walk = str;
1108        while( *walk )
1109        {
1110            const char * p = strchr( walk, ',' );
1111            if( !p )
1112                p = walk + strlen( walk );
1113            addToken( setme, walk, p - walk );
1114            if( *p != ',' )
1115                break;
1116            walk = p + 1;
1117        }
1118    }
1119
1120    tr_free( str );
1121}
1122
1123void
1124tr_rpc_request_exec_uri( tr_session           * session,
1125                         const void           * request_uri,
1126                         int                    request_len,
1127                         tr_rpc_response_func   callback,
1128                         void                 * callback_user_data )
1129{
1130    tr_benc      top, * args;
1131    char *       request = tr_strndup( request_uri, request_len );
1132    const char * pch;
1133
1134    tr_bencInitDict( &top, 3 );
1135    args = tr_bencDictAddDict( &top, "arguments", 0 );
1136
1137    pch = strchr( request, '?' );
1138    if( !pch ) pch = request;
1139    while( pch )
1140    {
1141        const char * delim = strchr( pch, '=' );
1142        const char * next = strchr( pch, '&' );
1143        if( delim )
1144        {
1145            char *    key = tr_strndup( pch, delim - pch );
1146            int       isArg = strcmp( key, "method" ) && strcmp( key, "tag" );
1147            tr_benc * parent = isArg ? args : &top;
1148            tr_rpc_parse_list_str( tr_bencDictAdd( parent, key ),
1149                                  delim + 1,
1150                                  next ? (size_t)(
1151                                       next -
1152                                      ( delim + 1 ) ) : strlen( delim + 1 ) );
1153            tr_free( key );
1154        }
1155        pch = next ? next + 1 : NULL;
1156    }
1157
1158    request_exec( session, &top, callback, callback_user_data );
1159
1160    /* cleanup */
1161    tr_bencFree( &top );
1162    tr_free( request );
1163}
Note: See TracBrowser for help on using the repository browser.