source: trunk/libtransmission/rpcimpl.c @ 7748

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

(trunk libT) prefer int' over ssize_t'

  • 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 7748 2009-01-19 14:05:43Z 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                          int                     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                       int           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                         int                    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.