source: trunk/libtransmission/rpcimpl.c @ 7744

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

(trunk libT) In RPC, add general support for nonblocking methods, and specific support for adding a torrent via its URL and fetching it via curl without blocking.

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