source: trunk/libtransmission/rpcimpl.c @ 7992

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

(trunk) #1608: Completed pieces bitfield in rpc

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