source: trunk/libtransmission/rpcimpl.c @ 8006

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

(trunk libT) when a "torrent-set" request is sent to change files' priorities and/or download flags, and some of the file indices are out-of-range, return an error message in the rpc response's "result" string.

  • Property svn:keywords set to Date Rev Author Id
File size: 38.8 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 8006 2009-03-03 20:06:45Z 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 const char*
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    const char * errmsg = NULL;
547    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
548
549    if( n )
550    {
551        for( i = 0; i < n; ++i ) {
552            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) {
553                if( 0 <= tmp && tmp < tor->info.fileCount ) {
554                    files[fileCount++] = tmp;
555                } else {
556                    errmsg = "file index out of range";
557                }
558            }
559        }
560    }
561    else /* if empty set, apply to all */
562    {
563        tr_file_index_t t;
564        for( t = 0; t < tor->info.fileCount; ++t )
565            files[fileCount++] = t;
566    }
567
568    if( fileCount )
569        tr_torrentSetFilePriorities( tor, files, fileCount, priority );
570
571    tr_free( files );
572    return errmsg;
573}
574
575static const char*
576setFileDLs( tr_torrent * tor,
577            int          do_download,
578            tr_benc *    list )
579{
580    int i;
581    int64_t tmp;
582    int fileCount = 0;
583    const int n = tr_bencListSize( list );
584    const char * errmsg = NULL;
585    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
586
587    if( n ) /* if argument list, process them */
588    {
589        for( i = 0; i < n; ++i ) {
590            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) {
591                if( 0 <= tmp && tmp < tor->info.fileCount ) {
592                    files[fileCount++] = tmp;
593                } else {
594                    errmsg = "file index out of range";
595                }
596            }
597        }
598    }
599    else /* if empty set, apply to all */
600    {
601        tr_file_index_t t;
602        for( t = 0; t < tor->info.fileCount; ++t )
603            files[fileCount++] = t;
604    }
605
606    if( fileCount )
607        tr_torrentSetFileDLs( tor, files, fileCount, do_download );
608
609    tr_free( files );
610    return errmsg;
611}
612
613static const char*
614torrentSet( tr_session               * session,
615            tr_benc                  * args_in,
616            tr_benc                  * args_out UNUSED,
617            struct tr_rpc_idle_data  * idle_data )
618{
619    const char * errmsg = NULL;
620    int i, torrentCount;
621    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
622
623    assert( idle_data == NULL );
624
625    for( i = 0; i < torrentCount; ++i )
626    {
627        int64_t      tmp;
628        double       d;
629        tr_benc *    files;
630        tr_torrent * tor = torrents[i];
631
632        if( tr_bencDictFindList( args_in, "files-unwanted", &files ) )
633            setFileDLs( tor, FALSE, files );
634        if( tr_bencDictFindList( args_in, "files-wanted", &files ) )
635            setFileDLs( tor, TRUE, files );
636        if( tr_bencDictFindInt( args_in, "peer-limit", &tmp ) )
637            tr_torrentSetPeerLimit( tor, tmp );
638        if( !errmsg &&  tr_bencDictFindList( args_in, "priority-high", &files ) )
639            errmsg = setFilePriorities( tor, TR_PRI_HIGH, files );
640        if( !errmsg && tr_bencDictFindList( args_in, "priority-low", &files ) )
641            errmsg = setFilePriorities( tor, TR_PRI_LOW, files );
642        if( !errmsg && tr_bencDictFindList( args_in, "priority-normal", &files ) )
643            errmsg = setFilePriorities( tor, TR_PRI_NORMAL, files );
644        if( tr_bencDictFindInt( args_in, "speed-limit-down", &tmp ) )
645            tr_torrentSetSpeedLimit( tor, TR_DOWN, tmp );
646        if( tr_bencDictFindInt( args_in, "speed-limit-down-enabled", &tmp ) )
647            tr_torrentSetSpeedMode(
648                tor, TR_DOWN, tmp ? TR_SPEEDLIMIT_SINGLE
649                : TR_SPEEDLIMIT_GLOBAL );
650        if( tr_bencDictFindInt( args_in, "speed-limit-up", &tmp ) )
651            tr_torrentSetSpeedLimit( tor, TR_UP, tmp );
652        if( tr_bencDictFindInt( args_in, "speed-limit-up-enabled", &tmp ) )
653            tr_torrentSetSpeedMode( tor, TR_UP, tmp ? TR_SPEEDLIMIT_SINGLE
654                                    : TR_SPEEDLIMIT_GLOBAL );
655        if( tr_bencDictFindDouble( args_in, "ratio-limit", &d ) )
656            tr_torrentSetRatioLimit( tor, d );
657        if( tr_bencDictFindInt( args_in, "ratio-limit-mode", &tmp ) )
658            tr_torrentSetRatioMode( tor, tmp );
659        notify( session, TR_RPC_TORRENT_CHANGED, tor );
660    }
661
662    tr_free( torrents );
663    return errmsg;
664}
665
666/***
667****
668***/
669
670static void
671addTorrentImpl( struct tr_rpc_idle_data * data, tr_ctor * ctor )
672{
673    int err = 0;
674    const char * result = NULL;
675    tr_torrent * tor = tr_torrentNew( data->session, ctor, &err );
676
677    tr_ctorFree( ctor );
678
679    if( tor )
680    {
681        tr_benc fields;
682        tr_bencInitList( &fields, 3 );
683        tr_bencListAddStr( &fields, "id" );
684        tr_bencListAddStr( &fields, "name" );
685        tr_bencListAddStr( &fields, "hashString" );
686        addInfo( tor, tr_bencDictAdd( data->args_out, "torrent-added" ), &fields );
687        notify( data->session, TR_RPC_TORRENT_ADDED, tor );
688        tr_bencFree( &fields );
689    }
690    else if( err == TR_EDUPLICATE )
691    {
692        result = "duplicate torrent";
693    }
694    else if( err == TR_EINVALID )
695    {
696        result = "invalid or corrupt torrent file";
697    }
698
699    tr_idle_function_done( data, result );
700}
701
702
703struct add_torrent_idle_data
704{
705    struct tr_rpc_idle_data * data;
706    tr_ctor * ctor;
707};
708
709static void
710gotMetadataFromURL( tr_session       * session UNUSED,
711                    long               response_code,
712                    const void       * response,
713                    size_t             response_byte_count,
714                    void             * user_data )
715{
716    struct add_torrent_idle_data * data = user_data;
717
718    dbgmsg( "torrentAdd: HTTP response code was %ld (%s); response length was %zu bytes",
719            response_code, tr_webGetResponseStr( response_code ), response_byte_count );
720
721    if( response_code == 200 )
722    {
723        tr_ctorSetMetainfo( data->ctor, response, response_byte_count );
724        addTorrentImpl( data->data, data->ctor );
725    }
726    else
727    {
728        char result[1024];
729        tr_snprintf( result, sizeof( result ), "http error %ld: %s",
730                     response_code, tr_webGetResponseStr( response_code ) );
731        tr_idle_function_done( data->data, result );
732    }
733
734    tr_free( data );
735}
736
737static tr_bool
738isCurlURL( const char * filename )
739{
740    if( filename == NULL )
741        return FALSE;
742
743    return ( strstr( filename, "ftp://" ) != NULL )
744        || ( strstr( filename, "http://" ) != NULL )
745        || ( strstr( filename, "https://" ) != NULL );
746}
747
748static const char*
749torrentAdd( tr_session               * session,
750            tr_benc                  * args_in,
751            tr_benc                  * args_out UNUSED,
752            struct tr_rpc_idle_data  * idle_data )
753{
754    const char * filename = NULL;
755    const char * metainfo_base64 = NULL;
756
757    assert( idle_data != NULL );
758
759    tr_bencDictFindStr( args_in, "filename", &filename );
760    tr_bencDictFindStr( args_in, "metainfo", &metainfo_base64 );
761    if( !filename && !metainfo_base64 )
762        return "no filename or metainfo specified";
763    else
764    {
765        int64_t      i;
766        const char * str;
767        tr_ctor    * ctor = tr_ctorNew( session );
768
769        /* set the optional arguments */
770        if( tr_bencDictFindStr( args_in, "download-dir", &str ) )
771            tr_ctorSetDownloadDir( ctor, TR_FORCE, str );
772        if( tr_bencDictFindInt( args_in, "paused", &i ) )
773            tr_ctorSetPaused( ctor, TR_FORCE, i );
774        if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
775            tr_ctorSetPeerLimit( ctor, TR_FORCE, i );
776
777        dbgmsg( "torrentAdd: filename is \"%s\"", filename );
778
779        if( isCurlURL( filename ) )
780        {
781            struct add_torrent_idle_data * d = tr_new0( struct add_torrent_idle_data, 1 );
782            d->data = idle_data;
783            d->ctor = ctor;
784            tr_webRun( session, filename, NULL, gotMetadataFromURL, d );
785        }
786        else
787        {
788            if( filename != NULL )
789                tr_ctorSetMetainfoFromFile( ctor, filename );
790            else {
791                int len;
792                char * metainfo = tr_base64_decode( metainfo_base64, -1,  &len );
793                tr_ctorSetMetainfo( ctor, (uint8_t*)metainfo, len );
794                tr_free( metainfo );
795            }
796            addTorrentImpl( idle_data, ctor );
797        }
798
799    }
800
801    return NULL;
802}
803
804/***
805****
806***/
807
808static const char*
809sessionSet( tr_session               * session,
810            tr_benc                  * args_in,
811            tr_benc                  * args_out UNUSED,
812            struct tr_rpc_idle_data  * idle_data )
813{
814    int64_t      i;
815    double       d;
816    const char * str;
817
818    assert( idle_data == NULL );
819
820    if( tr_bencDictFindStr( args_in, "download-dir", &str ) )
821        tr_sessionSetDownloadDir( session, str );
822    if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
823        tr_sessionSetPeerLimit( session, i );
824    if( tr_bencDictFindInt( args_in, "pex-allowed", &i ) )
825        tr_sessionSetPexEnabled( session, i );
826    if( tr_bencDictFindInt( args_in, "port", &i ) )
827        tr_sessionSetPeerPort( session, i );
828    if( tr_bencDictFindInt( args_in, "port-forwarding-enabled", &i ) )
829        tr_sessionSetPortForwardingEnabled( session, i );
830    if( tr_bencDictFindInt( args_in, "speed-limit-down", &i ) )
831        tr_sessionSetSpeedLimit( session, TR_DOWN, i );
832    if( tr_bencDictFindInt( args_in, "speed-limit-down-enabled", &i ) )
833        tr_sessionSetSpeedLimitEnabled( session, TR_DOWN, i );
834    if( tr_bencDictFindInt( args_in, "speed-limit-up", &i ) )
835        tr_sessionSetSpeedLimit( session, TR_UP, i );
836    if( tr_bencDictFindInt( args_in, "speed-limit-up-enabled", &i ) )
837        tr_sessionSetSpeedLimitEnabled( session, TR_UP, i );
838    if( tr_bencDictFindDouble( args_in, "ratio-limit", &d ) )
839        tr_sessionSetRatioLimit( session, d );
840    if( tr_bencDictFindInt( args_in, "ratio-limit-enabled", &i ) )
841        tr_sessionSetRatioLimited( session, i );
842    if( tr_bencDictFindStr( args_in, "encryption", &str ) )
843    {
844        if( !strcmp( str, "required" ) )
845            tr_sessionSetEncryption( session, TR_ENCRYPTION_REQUIRED );
846        else if( !strcmp( str, "tolerated" ) )
847            tr_sessionSetEncryption( session, TR_CLEAR_PREFERRED );
848        else
849            tr_sessionSetEncryption( session, TR_ENCRYPTION_PREFERRED );
850    }
851
852    notify( session, TR_RPC_SESSION_CHANGED, NULL );
853
854    return NULL;
855}
856
857static const char*
858sessionStats( tr_session               * session,
859              tr_benc                  * args_in UNUSED,
860              tr_benc                  * args_out,
861              struct tr_rpc_idle_data  * idle_data )
862{
863    int running = 0;
864    int total = 0;
865    tr_benc * d; 
866    tr_session_stats currentStats = { 0.0f, 0, 0, 0, 0, 0 }; 
867    tr_session_stats cumulativeStats = { 0.0f, 0, 0, 0, 0, 0 }; 
868    tr_torrent * tor = NULL;
869
870    assert( idle_data == NULL );
871
872    while(( tor = tr_torrentNext( session, tor ))) {
873        ++total;
874        if( tor->isRunning )
875            ++running;
876    }
877
878    tr_sessionGetStats( session, &currentStats ); 
879    tr_sessionGetCumulativeStats( session, &cumulativeStats ); 
880
881    tr_bencDictAddInt( args_out, "activeTorrentCount", running );
882    tr_bencDictAddInt( args_out, "downloadSpeed", (int)( tr_sessionGetPieceSpeed( session, TR_DOWN ) * 1024 ) );
883    tr_bencDictAddInt( args_out, "pausedTorrentCount", total - running );
884    tr_bencDictAddInt( args_out, "torrentCount", total );
885    tr_bencDictAddInt( args_out, "uploadSpeed", (int)( tr_sessionGetPieceSpeed( session, TR_UP ) * 1024 ) );
886
887    d = tr_bencDictAddDict( args_out, "cumulative-stats", 5 ); 
888    tr_bencDictAddInt( d, "downloadedBytes", cumulativeStats.downloadedBytes ); 
889    tr_bencDictAddInt( d, "filesAdded", cumulativeStats.filesAdded ); 
890    tr_bencDictAddInt( d, "secondsActive", cumulativeStats.secondsActive ); 
891    tr_bencDictAddInt( d, "sessionCount", cumulativeStats.sessionCount ); 
892    tr_bencDictAddInt( d, "uploadedBytes", cumulativeStats.uploadedBytes ); 
893
894    d = tr_bencDictAddDict( args_out, "current-stats", 5 ); 
895    tr_bencDictAddInt( d, "downloadedBytes", currentStats.downloadedBytes ); 
896    tr_bencDictAddInt( d, "filesAdded", currentStats.filesAdded ); 
897    tr_bencDictAddInt( d, "secondsActive", currentStats.secondsActive ); 
898    tr_bencDictAddInt( d, "sessionCount", currentStats.sessionCount ); 
899    tr_bencDictAddInt( d, "uploadedBytes", currentStats.uploadedBytes ); 
900
901    return NULL;
902}
903
904static const char*
905sessionGet( tr_session               * session,
906            tr_benc                  * args_in UNUSED,
907            tr_benc                  * args_out,
908            struct tr_rpc_idle_data  * idle_data )
909{
910    const char * str;
911    tr_benc *    d = args_out;
912
913    assert( idle_data == NULL );
914
915    tr_bencDictAddStr( d, "download-dir", tr_sessionGetDownloadDir( session ) );
916    tr_bencDictAddInt( d, "peer-limit", tr_sessionGetPeerLimit( session ) );
917    tr_bencDictAddInt( d, "pex-allowed", tr_sessionIsPexEnabled( session ) );
918    tr_bencDictAddInt( d, "port", tr_sessionGetPeerPort( session ) );
919    tr_bencDictAddInt( d, "port-forwarding-enabled", tr_sessionIsPortForwardingEnabled( session ) );
920    tr_bencDictAddInt( d, "rpc-version", 4 );
921    tr_bencDictAddInt( d, "rpc-version-minimum", 1 );
922    tr_bencDictAddInt( d, "speed-limit-up", tr_sessionGetSpeedLimit( session, TR_UP ) );
923    tr_bencDictAddInt( d, "speed-limit-up-enabled", tr_sessionIsSpeedLimitEnabled( session, TR_UP ) );
924    tr_bencDictAddInt( d, "speed-limit-down", tr_sessionGetSpeedLimit( session, TR_DOWN ) );
925    tr_bencDictAddInt( d, "speed-limit-down-enabled", tr_sessionIsSpeedLimitEnabled( session, TR_DOWN ) );
926    tr_bencDictAddDouble( d, "ratio-limit", tr_sessionGetRatioLimit( session ) );
927    tr_bencDictAddInt( d, "ratio-limit-enabled", tr_sessionIsRatioLimited( session ) );
928    tr_bencDictAddStr( d, "version", LONG_VERSION_STRING );
929    switch( tr_sessionGetEncryption( session ) ) {
930        case TR_CLEAR_PREFERRED: str = "tolerated"; break;
931        case TR_ENCRYPTION_REQUIRED: str = "required"; break; 
932        default: str = "preferred"; break;
933    }
934    tr_bencDictAddStr( d, "encryption", str );
935
936    return NULL;
937}
938
939/***
940****
941***/
942
943typedef const char* ( *handler )( tr_session*, tr_benc*, tr_benc*, struct tr_rpc_idle_data * );
944
945static struct method
946{
947    const char *  name;
948    tr_bool       immediate;
949    handler       func;
950}
951methods[] =
952{
953    { "session-get",    TRUE,  sessionGet          },
954    { "session-set",    TRUE,  sessionSet          },
955    { "session-stats",  TRUE,  sessionStats        },
956    { "torrent-add",    FALSE, torrentAdd          },
957    { "torrent-get",    TRUE,  torrentGet          },
958    { "torrent-remove", TRUE,  torrentRemove       },
959    { "torrent-set",    TRUE,  torrentSet          },
960    { "torrent-start",  TRUE,  torrentStart        },
961    { "torrent-stop",   TRUE,  torrentStop         },
962    { "torrent-verify", TRUE,  torrentVerify       }
963};
964
965static void
966noop_response_callback( tr_session * session UNUSED,
967                        const char * response UNUSED,
968                        size_t       response_len UNUSED,
969                        void       * user_data UNUSED )
970{
971}
972
973static void
974request_exec( tr_session             * session,
975              tr_benc                * request,
976              tr_rpc_response_func     callback,
977              void                   * callback_user_data )
978{
979    int i;
980    const char * str;
981    tr_benc * args_in = tr_bencDictFind( request, "arguments" );
982    const char * result = NULL;
983
984    if( callback == NULL )
985        callback = noop_response_callback;
986
987    /* parse the request */
988    if( !tr_bencDictFindStr( request, "method", &str ) )
989        result = "no method name";
990    else {
991        const int n = TR_N_ELEMENTS( methods );
992        for( i = 0; i < n; ++i )
993            if( !strcmp( str, methods[i].name ) )
994                break;
995        if( i ==n )
996            result = "method name not recognized";
997    }
998
999    /* if we couldn't figure out which method to use, return an error */
1000    if( result != NULL )
1001    {
1002        int64_t tag;
1003        tr_benc response;
1004        struct evbuffer * buf = tr_getBuffer( );
1005
1006        tr_bencInitDict( &response, 3 );
1007        tr_bencDictAddDict( &response, "arguments", 0 );
1008        tr_bencDictAddStr( &response, "result", result );
1009        if( tr_bencDictFindInt( request, "tag", &tag ) )
1010            tr_bencDictAddInt( &response, "tag", tag );
1011        tr_bencSaveAsJSON( &response, buf );
1012        (*callback)( session, (const char*)EVBUFFER_DATA(buf),
1013                     EVBUFFER_LENGTH( buf ), callback_user_data );
1014
1015        tr_releaseBuffer( buf );
1016        tr_bencFree( &response );
1017    }
1018    else if( methods[i].immediate )
1019    {
1020        int64_t tag;
1021        tr_benc response;
1022        tr_benc * args_out;
1023        struct evbuffer * buf = tr_getBuffer( );
1024
1025        tr_bencInitDict( &response, 3 );
1026        args_out = tr_bencDictAddDict( &response, "arguments", 0 );
1027        result = (*methods[i].func)( session, args_in, args_out, NULL );
1028        if( result == NULL )
1029            result = "success";
1030        tr_bencDictAddStr( &response, "result", result );
1031        if( tr_bencDictFindInt( request, "tag", &tag ) )
1032            tr_bencDictAddInt( &response, "tag", tag );
1033        tr_bencSaveAsJSON( &response, buf );
1034        (*callback)( session, (const char*)EVBUFFER_DATA(buf),
1035                     EVBUFFER_LENGTH(buf), callback_user_data );
1036
1037        tr_releaseBuffer( buf );
1038        tr_bencFree( &response );
1039    }
1040    else
1041    {
1042        int64_t tag;
1043        struct tr_rpc_idle_data * data = tr_new0( struct tr_rpc_idle_data, 1 );
1044        data->session = session;
1045        data->response = tr_new0( tr_benc, 1 );
1046        tr_bencInitDict( data->response, 3 );
1047        if( tr_bencDictFindInt( request, "tag", &tag ) )
1048            tr_bencDictAddInt( data->response, "tag", tag );
1049        data->args_out = tr_bencDictAddDict( data->response, "arguments", 0 );
1050        data->callback = callback;
1051        data->callback_user_data = callback_user_data;
1052        (*methods[i].func)( session, args_in, data->args_out, data );
1053    }
1054}
1055
1056void
1057tr_rpc_request_exec_json( tr_session            * session,
1058                          const void            * request_json,
1059                          int                     request_len,
1060                          tr_rpc_response_func    callback,
1061                          void                  * callback_user_data )
1062{
1063    tr_benc top;
1064    int have_content;
1065
1066    if( request_len < 0 )
1067        request_len = strlen( request_json );
1068
1069    have_content = !tr_jsonParse( request_json, request_len, &top, NULL );
1070    request_exec( session, have_content ? &top : NULL, callback, callback_user_data );
1071
1072    if( have_content )
1073        tr_bencFree( &top );
1074}
1075
1076/**
1077 * Munge the URI into a usable form.
1078 *
1079 * We have very loose typing on this to make the URIs as simple as possible:
1080 * - anything not a 'tag' or 'method' is automatically in 'arguments'
1081 * - values that are all-digits are numbers
1082 * - values that are all-digits or commas are number lists
1083 * - all other values are strings
1084 */
1085void
1086tr_rpc_parse_list_str( tr_benc     * setme,
1087                       const char  * str,
1088                       int           len )
1089
1090{
1091    int valueCount;
1092    int * values = tr_parseNumberRange( str, len, &valueCount );
1093
1094    if( valueCount == 0 )
1095        tr_bencInitStr( setme, str, len );
1096    else if( valueCount == 1 )
1097        tr_bencInitInt( setme, values[0] );
1098    else {
1099        int i;
1100        tr_bencInitList( setme, valueCount );
1101        for( i=0; i<valueCount; ++i )
1102            tr_bencListAddInt( setme, values[i] );
1103    }
1104
1105    tr_free( values );
1106}
1107
1108void
1109tr_rpc_request_exec_uri( tr_session           * session,
1110                         const void           * request_uri,
1111                         int                    request_len,
1112                         tr_rpc_response_func   callback,
1113                         void                 * callback_user_data )
1114{
1115    tr_benc      top, * args;
1116    char *       request = tr_strndup( request_uri, request_len );
1117    const char * pch;
1118
1119    tr_bencInitDict( &top, 3 );
1120    args = tr_bencDictAddDict( &top, "arguments", 0 );
1121
1122    pch = strchr( request, '?' );
1123    if( !pch ) pch = request;
1124    while( pch )
1125    {
1126        const char * delim = strchr( pch, '=' );
1127        const char * next = strchr( pch, '&' );
1128        if( delim )
1129        {
1130            char *    key = tr_strndup( pch, delim - pch );
1131            int       isArg = strcmp( key, "method" ) && strcmp( key, "tag" );
1132            tr_benc * parent = isArg ? args : &top;
1133            tr_rpc_parse_list_str( tr_bencDictAdd( parent, key ),
1134                                  delim + 1,
1135                                  next ? (size_t)(
1136                                       next -
1137                                      ( delim + 1 ) ) : strlen( delim + 1 ) );
1138            tr_free( key );
1139        }
1140        pch = next ? next + 1 : NULL;
1141    }
1142
1143    request_exec( session, &top, callback, callback_user_data );
1144
1145    /* cleanup */
1146    tr_bencFree( &top );
1147    tr_free( request );
1148}
Note: See TracBrowser for help on using the repository browser.