source: trunk/libtransmission/rpcimpl.c @ 11599

Last change on this file since 11599 was 11599, checked in by charles, 11 years ago

(trunk) Join the 21st century and use only 1 space at the end sentences. This commit is nearly as important as the semi-annual ones that remove trailing spaces from the ends of lines of code... :)

  • Property svn:keywords set to Date Rev Author Id
File size: 64.7 KB
Line 
1/*
2 * This file Copyright (C) 2008-2010 Mnemosyne LLC
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 11599 2010-12-27 19:18:17Z charles $
11 */
12
13#include <assert.h>
14#include <ctype.h> /* isdigit */
15#include <errno.h>
16#include <stdlib.h> /* strtol */
17#include <string.h> /* strcmp */
18#include <unistd.h> /* unlink */
19
20#ifdef HAVE_ZLIB
21 #include <zlib.h>
22#endif
23
24#include <event2/buffer.h>
25
26#include "transmission.h"
27#include "bencode.h"
28#include "completion.h"
29#include "fdlimit.h"
30#include "json.h"
31#include "rpcimpl.h"
32#include "session.h"
33#include "stats.h"
34#include "torrent.h"
35#include "utils.h"
36#include "version.h"
37#include "web.h"
38
39#define RPC_VERSION     11
40#define RPC_VERSION_MIN 1
41
42#define RECENTLY_ACTIVE_SECONDS 60
43
44#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) )
45
46#if 0
47#define dbgmsg(fmt, ...) \
48    do { \
49        fprintf( stderr, "%s:%d"#fmt, __FILE__, __LINE__, __VA_ARGS__ ); \
50        fprintf( stderr, "\n" ); \
51    } while( 0 )
52#else
53#define dbgmsg( ... ) \
54    do { \
55        if( tr_deepLoggingIsActive( ) ) \
56            tr_deepLog( __FILE__, __LINE__, "RPC", __VA_ARGS__ ); \
57    } while( 0 )
58#endif
59
60
61/***
62****
63***/
64
65static tr_rpc_callback_status
66notify( tr_session * session,
67        int          type,
68        tr_torrent * tor )
69{
70    tr_rpc_callback_status status = 0;
71
72    if( session->rpc_func )
73        status = session->rpc_func( session, type, tor,
74                                    session->rpc_func_user_data );
75
76    return status;
77}
78
79/***
80****
81***/
82
83/* For functions that can't be immediately executed, like torrentAdd,
84 * this is the callback data used to pass a response to the caller
85 * when the task is complete */
86struct tr_rpc_idle_data
87{
88    tr_session            * session;
89    tr_benc               * response;
90    tr_benc               * args_out;
91    tr_rpc_response_func    callback;
92    void                  * callback_user_data;
93};
94
95static void
96tr_idle_function_done( struct tr_rpc_idle_data * data, const char * result )
97{
98    struct evbuffer * buf = evbuffer_new( );
99
100    if( result == NULL )
101        result = "success";
102    tr_bencDictAddStr( data->response, "result", result );
103
104    tr_bencToBuf( data->response, TR_FMT_JSON_LEAN, buf );
105    (*data->callback)( data->session, buf, data->callback_user_data );
106
107    evbuffer_free( buf );
108    tr_bencFree( data->response );
109    tr_free( data->response );
110    tr_free( data );
111}
112
113/***
114****
115***/
116
117static tr_torrent **
118getTorrents( tr_session * session,
119             tr_benc    * args,
120             int        * setmeCount )
121{
122    int           torrentCount = 0;
123    int64_t       id;
124    tr_torrent ** torrents = NULL;
125    tr_benc *     ids;
126    const char * str;
127
128    if( tr_bencDictFindList( args, "ids", &ids ) )
129    {
130        int       i;
131        const int n = tr_bencListSize( ids );
132
133        torrents = tr_new0( tr_torrent *, n );
134
135        for( i = 0; i < n; ++i )
136        {
137            tr_torrent * tor = NULL;
138            tr_benc *    node = tr_bencListChild( ids, i );
139            const char * str;
140            if( tr_bencGetInt( node, &id ) )
141                tor = tr_torrentFindFromId( session, id );
142            else if( tr_bencGetStr( node, &str ) )
143                tor = tr_torrentFindFromHashString( session, str );
144            if( tor )
145                torrents[torrentCount++] = tor;
146        }
147    }
148    else if( tr_bencDictFindInt( args, "ids", &id )
149           || tr_bencDictFindInt( args, "id", &id ) )
150    {
151        tr_torrent * tor;
152        torrents = tr_new0( tr_torrent *, 1 );
153        if( ( tor = tr_torrentFindFromId( session, id ) ) )
154            torrents[torrentCount++] = tor;
155    }
156    else if( tr_bencDictFindStr( args, "ids", &str ) )
157    {
158        if( !strcmp( str, "recently-active" ) )
159        {
160            tr_torrent * tor = NULL;
161            const time_t now = tr_time( );
162            const time_t window = RECENTLY_ACTIVE_SECONDS;
163            const int n = tr_sessionCountTorrents( session );
164            torrents = tr_new0( tr_torrent *, n );
165            while( ( tor = tr_torrentNext( session, tor ) ) )
166                if( tor->anyDate >= now - window )
167                    torrents[torrentCount++] = tor;
168        }
169        else
170        {
171            tr_torrent * tor;
172            torrents = tr_new0( tr_torrent *, 1 );
173            if(( tor = tr_torrentFindFromHashString( session, str )))
174                torrents[torrentCount++] = tor;
175        }
176    }
177    else /* all of them */
178    {
179        tr_torrent * tor = NULL;
180        const int n = tr_sessionCountTorrents( session );
181        torrents = tr_new0( tr_torrent *, n );
182        while( ( tor = tr_torrentNext( session, tor ) ) )
183            torrents[torrentCount++] = tor;
184    }
185
186    *setmeCount = torrentCount;
187    return torrents;
188}
189
190static const char*
191torrentStart( tr_session               * session,
192              tr_benc                  * args_in,
193              tr_benc                  * args_out UNUSED,
194              struct tr_rpc_idle_data  * idle_data UNUSED )
195{
196    int           i, torrentCount;
197    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
198
199    assert( idle_data == NULL );
200
201    for( i = 0; i < torrentCount; ++i )
202    {
203        tr_torrent * tor = torrents[i];
204        if( !tor->isRunning )
205        {
206            tr_torrentStart( tor );
207            notify( session, TR_RPC_TORRENT_STARTED, tor );
208        }
209    }
210    tr_free( torrents );
211    return NULL;
212}
213
214static const char*
215torrentStop( tr_session               * session,
216             tr_benc                  * args_in,
217             tr_benc                  * args_out UNUSED,
218             struct tr_rpc_idle_data  * idle_data UNUSED )
219{
220    int           i, torrentCount;
221    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
222
223    assert( idle_data == NULL );
224
225    for( i = 0; i < torrentCount; ++i )
226    {
227        tr_torrent * tor = torrents[i];
228
229        if( tor->isRunning )
230        {
231            tor->isStopping = TRUE;
232            notify( session, TR_RPC_TORRENT_STOPPED, tor );
233        }
234    }
235    tr_free( torrents );
236    return NULL;
237}
238
239static const char*
240torrentRemove( tr_session               * session,
241               tr_benc                  * args_in,
242               tr_benc                  * args_out UNUSED,
243               struct tr_rpc_idle_data  * idle_data UNUSED )
244{
245    int i;
246    int torrentCount;
247    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
248
249    assert( idle_data == NULL );
250
251    for( i=0; i<torrentCount; ++i )
252    {
253        tr_torrent * tor = torrents[i];
254        const tr_rpc_callback_status status = notify( session, TR_RPC_TORRENT_REMOVING, tor );
255        if( !( status & TR_RPC_NOREMOVE ) )
256        {
257            tr_bool deleteFlag = FALSE;
258            tr_bencDictFindBool( args_in, "delete-local-data", &deleteFlag );
259            tr_torrentRemove( tor, deleteFlag, NULL );
260        }
261    }
262
263    tr_free( torrents );
264    return NULL;
265}
266
267static const char*
268torrentReannounce( tr_session               * session,
269                   tr_benc                  * args_in,
270                   tr_benc                  * args_out UNUSED,
271                   struct tr_rpc_idle_data  * idle_data UNUSED )
272{
273    int i, torrentCount;
274    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
275
276    assert( idle_data == NULL );
277
278    for( i=0; i<torrentCount; ++i )
279    {
280        tr_torrent * tor = torrents[i];
281        if( tr_torrentCanManualUpdate( tor ) )
282        {
283            tr_torrentManualUpdate( tor );
284            notify( session, TR_RPC_TORRENT_CHANGED, tor );
285        }
286    }
287
288    tr_free( torrents );
289    return NULL;
290}
291
292static const char*
293torrentVerify( tr_session               * session,
294               tr_benc                  * args_in,
295               tr_benc                  * args_out UNUSED,
296               struct tr_rpc_idle_data  * idle_data UNUSED )
297{
298    int           i, torrentCount;
299    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
300
301    assert( idle_data == NULL );
302
303    for( i = 0; i < torrentCount; ++i )
304    {
305        tr_torrent * tor = torrents[i];
306        tr_torrentVerify( tor );
307        notify( session, TR_RPC_TORRENT_CHANGED, tor );
308    }
309
310    tr_free( torrents );
311    return NULL;
312}
313
314/***
315****
316***/
317
318static void
319addFileStats( const tr_torrent * tor, tr_benc * list )
320{
321    tr_file_index_t i;
322    tr_file_index_t n;
323    const tr_info * info = tr_torrentInfo( tor );
324    tr_file_stat * files = tr_torrentFiles( tor, &n );
325
326    for( i = 0; i < info->fileCount; ++i )
327    {
328        const tr_file * file = &info->files[i];
329        tr_benc * d = tr_bencListAddDict( list, 3 );
330        tr_bencDictAddInt( d, "bytesCompleted", files[i].bytesCompleted );
331        tr_bencDictAddInt( d, "priority", file->priority );
332        tr_bencDictAddBool( d, "wanted", !file->dnd );
333    }
334
335    tr_torrentFilesFree( files, n );
336}
337
338static void
339addFiles( const tr_torrent * tor,
340          tr_benc *          list )
341{
342    tr_file_index_t i;
343    tr_file_index_t n;
344    const tr_info * info = tr_torrentInfo( tor );
345    tr_file_stat *  files = tr_torrentFiles( tor, &n );
346
347    for( i = 0; i < info->fileCount; ++i )
348    {
349        const tr_file * file = &info->files[i];
350        tr_benc *       d = tr_bencListAddDict( list, 3 );
351        tr_bencDictAddInt( d, "bytesCompleted", files[i].bytesCompleted );
352        tr_bencDictAddInt( d, "length", file->length );
353        tr_bencDictAddStr( d, "name", file->name );
354    }
355
356    tr_torrentFilesFree( files, n );
357}
358
359static void
360addWebseeds( const tr_info * info,
361             tr_benc *       webseeds )
362{
363    int i;
364
365    for( i = 0; i < info->webseedCount; ++i )
366        tr_bencListAddStr( webseeds, info->webseeds[i] );
367}
368
369static void
370addTrackers( const tr_info * info,
371             tr_benc *       trackers )
372{
373    int i;
374
375    for( i = 0; i < info->trackerCount; ++i )
376    {
377        const tr_tracker_info * t = &info->trackers[i];
378        tr_benc *               d = tr_bencListAddDict( trackers, 4 );
379        tr_bencDictAddStr( d, "announce", t->announce );
380        tr_bencDictAddInt( d, "id", t->id );
381        tr_bencDictAddStr( d, "scrape", t->scrape );
382        tr_bencDictAddInt( d, "tier", t->tier );
383    }
384}
385
386static void
387addTrackerStats( const tr_tracker_stat * st, int n, tr_benc * list )
388{
389    int i;
390
391    for( i=0; i<n; ++i )
392    {
393        const tr_tracker_stat * s = &st[i];
394        tr_benc * d = tr_bencListAddDict( list, 26 );
395        tr_bencDictAddStr ( d, "announce", s->announce );
396        tr_bencDictAddInt ( d, "announceState", s->announceState );
397        tr_bencDictAddInt ( d, "downloadCount", s->downloadCount );
398        tr_bencDictAddBool( d, "hasAnnounced", s->hasAnnounced );
399        tr_bencDictAddBool( d, "hasScraped", s->hasScraped );
400        tr_bencDictAddStr ( d, "host", s->host );
401        tr_bencDictAddInt ( d, "id", s->id );
402        tr_bencDictAddBool( d, "isBackup", s->isBackup );
403        tr_bencDictAddInt ( d, "lastAnnouncePeerCount", s->lastAnnouncePeerCount );
404        tr_bencDictAddStr ( d, "lastAnnounceResult", s->lastAnnounceResult );
405        tr_bencDictAddInt ( d, "lastAnnounceStartTime", s->lastAnnounceStartTime );
406        tr_bencDictAddBool( d, "lastAnnounceSucceeded", s->lastAnnounceSucceeded );
407        tr_bencDictAddInt ( d, "lastAnnounceTime", s->lastAnnounceTime );
408        tr_bencDictAddBool( d, "lastAnnounceTimedOut", s->lastAnnounceTimedOut );
409        tr_bencDictAddStr ( d, "lastScrapeResult", s->lastScrapeResult );
410        tr_bencDictAddInt ( d, "lastScrapeStartTime", s->lastScrapeStartTime );
411        tr_bencDictAddBool( d, "lastScrapeSucceeded", s->lastScrapeSucceeded );
412        tr_bencDictAddInt ( d, "lastScrapeTime", s->lastScrapeTime );
413        tr_bencDictAddInt ( d, "lastScrapeTimedOut", s->lastScrapeTimedOut );
414        tr_bencDictAddInt ( d, "leecherCount", s->leecherCount );
415        tr_bencDictAddInt ( d, "nextAnnounceTime", s->nextAnnounceTime );
416        tr_bencDictAddInt ( d, "nextScrapeTime", s->nextScrapeTime );
417        tr_bencDictAddStr ( d, "scrape", s->scrape );
418        tr_bencDictAddInt ( d, "scrapeState", s->scrapeState );
419        tr_bencDictAddInt ( d, "seederCount", s->seederCount );
420        tr_bencDictAddInt ( d, "tier", s->tier );
421    }
422}
423
424static void
425addPeers( const tr_torrent * tor,
426          tr_benc *          list )
427{
428    int            i;
429    int            peerCount;
430    tr_peer_stat * peers = tr_torrentPeers( tor, &peerCount );
431
432    tr_bencInitList( list, peerCount );
433
434    for( i = 0; i < peerCount; ++i )
435    {
436        tr_benc *            d = tr_bencListAddDict( list, 14 );
437        const tr_peer_stat * peer = peers + i;
438        tr_bencDictAddStr ( d, "address", peer->addr );
439        tr_bencDictAddStr ( d, "clientName", peer->client );
440        tr_bencDictAddBool( d, "clientIsChoked", peer->clientIsChoked );
441        tr_bencDictAddBool( d, "clientIsInterested", peer->clientIsInterested );
442        tr_bencDictAddStr ( d, "flagStr", peer->flagStr );
443        tr_bencDictAddBool( d, "isDownloadingFrom", peer->isDownloadingFrom );
444        tr_bencDictAddBool( d, "isEncrypted", peer->isEncrypted );
445        tr_bencDictAddBool( d, "isIncoming", peer->isIncoming );
446        tr_bencDictAddBool( d, "isUploadingTo", peer->isUploadingTo );
447        tr_bencDictAddBool( d, "peerIsChoked", peer->peerIsChoked );
448        tr_bencDictAddBool( d, "peerIsInterested", peer->peerIsInterested );
449        tr_bencDictAddInt ( d, "port", peer->port );
450        tr_bencDictAddReal( d, "progress", peer->progress );
451        tr_bencDictAddInt ( d, "rateToClient", toSpeedBytes( peer->rateToClient_KBps ) );
452        tr_bencDictAddInt ( d, "rateToPeer", toSpeedBytes( peer->rateToPeer_KBps ) );
453    }
454
455    tr_torrentPeersFree( peers, peerCount );
456}
457
458/* faster-than-strcmp() optimization. This is kind of clumsy,
459   but addField() was in the profiler's top 10 list, and this
460   makes it 4x faster... */
461#define tr_streq(a,alen,b) ((alen+1==sizeof(b)) && !memcmp(a,b,alen))
462
463static void
464addField( const tr_torrent * tor, tr_benc * d, const char * key )
465{
466    const tr_info * inf = tr_torrentInfo( tor );
467    const tr_stat * st = tr_torrentStat( (tr_torrent*)tor );
468    const size_t keylen = strlen( key );
469
470    if( tr_streq( key, keylen, "activityDate" ) )
471        tr_bencDictAddInt( d, key, st->activityDate );
472    else if( tr_streq( key, keylen, "addedDate" ) )
473        tr_bencDictAddInt( d, key, st->addedDate );
474    else if( tr_streq( key, keylen, "bandwidthPriority" ) )
475        tr_bencDictAddInt( d, key, tr_torrentGetPriority( tor ) );
476    else if( tr_streq( key, keylen, "comment" ) )
477        tr_bencDictAddStr( d, key, inf->comment ? inf->comment : "" );
478    else if( tr_streq( key, keylen, "corruptEver" ) )
479        tr_bencDictAddInt( d, key, st->corruptEver );
480    else if( tr_streq( key, keylen, "creator" ) )
481        tr_bencDictAddStr( d, key, inf->creator ? inf->creator : "" );
482    else if( tr_streq( key, keylen, "dateCreated" ) )
483        tr_bencDictAddInt( d, key, inf->dateCreated );
484    else if( tr_streq( key, keylen, "desiredAvailable" ) )
485        tr_bencDictAddInt( d, key, st->desiredAvailable );
486    else if( tr_streq( key, keylen, "doneDate" ) )
487        tr_bencDictAddInt( d, key, st->doneDate );
488    else if( tr_streq( key, keylen, "downloadDir" ) )
489        tr_bencDictAddStr( d, key, tr_torrentGetDownloadDir( tor ) );
490    else if( tr_streq( key, keylen, "downloadedEver" ) )
491        tr_bencDictAddInt( d, key, st->downloadedEver );
492    else if( tr_streq( key, keylen, "downloadLimit" ) )
493        tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit_KBps( tor, TR_DOWN ) );
494    else if( tr_streq( key, keylen, "downloadLimited" ) )
495        tr_bencDictAddBool( d, key, tr_torrentUsesSpeedLimit( tor, TR_DOWN ) );
496    else if( tr_streq( key, keylen, "error" ) )
497        tr_bencDictAddInt( d, key, st->error );
498    else if( tr_streq( key, keylen, "errorString" ) )
499        tr_bencDictAddStr( d, key, st->errorString );
500    else if( tr_streq( key, keylen, "eta" ) )
501        tr_bencDictAddInt( d, key, st->eta );
502    else if( tr_streq( key, keylen, "files" ) )
503        addFiles( tor, tr_bencDictAddList( d, key, inf->fileCount ) );
504    else if( tr_streq( key, keylen, "fileStats" ) )
505        addFileStats( tor, tr_bencDictAddList( d, key, inf->fileCount ) );
506    else if( tr_streq( key, keylen, "hashString" ) )
507        tr_bencDictAddStr( d, key, tor->info.hashString );
508    else if( tr_streq( key, keylen, "haveUnchecked" ) )
509        tr_bencDictAddInt( d, key, st->haveUnchecked );
510    else if( tr_streq( key, keylen, "haveValid" ) )
511        tr_bencDictAddInt( d, key, st->haveValid );
512    else if( tr_streq( key, keylen, "honorsSessionLimits" ) )
513        tr_bencDictAddBool( d, key, tr_torrentUsesSessionLimits( tor ) );
514    else if( tr_streq( key, keylen, "id" ) )
515        tr_bencDictAddInt( d, key, st->id );
516    else if( tr_streq( key, keylen, "isFinished" ) )
517        tr_bencDictAddBool( d, key, st->finished );
518    else if( tr_streq( key, keylen, "isPrivate" ) )
519        tr_bencDictAddBool( d, key, tr_torrentIsPrivate( tor ) );
520    else if( tr_streq( key, keylen, "leftUntilDone" ) )
521        tr_bencDictAddInt( d, key, st->leftUntilDone );
522    else if( tr_streq( key, keylen, "manualAnnounceTime" ) )
523        tr_bencDictAddInt( d, key, st->manualAnnounceTime );
524    else if( tr_streq( key, keylen, "maxConnectedPeers" ) )
525        tr_bencDictAddInt( d, key,  tr_torrentGetPeerLimit( tor ) );
526    else if( tr_streq( key, keylen, "magnetLink" ) ) {
527        char * str = tr_torrentGetMagnetLink( tor );
528        tr_bencDictAddStr( d, key, str );
529        tr_free( str );
530    }
531    else if( tr_streq( key, keylen, "metadataPercentComplete" ) )
532        tr_bencDictAddReal( d, key, st->metadataPercentComplete );
533    else if( tr_streq( key, keylen, "name" ) )
534        tr_bencDictAddStr( d, key, inf->name );
535    else if( tr_streq( key, keylen, "percentDone" ) )
536        tr_bencDictAddReal( d, key, st->percentDone );
537    else if( tr_streq( key, keylen, "peer-limit" ) )
538        tr_bencDictAddInt( d, key, tr_torrentGetPeerLimit( tor ) );
539    else if( tr_streq( key, keylen, "peers" ) )
540        addPeers( tor, tr_bencDictAdd( d, key ) );
541    else if( tr_streq( key, keylen, "peersConnected" ) )
542        tr_bencDictAddInt( d, key, st->peersConnected );
543    else if( tr_streq( key, keylen, "peersFrom" ) )
544    {
545        tr_benc *   tmp = tr_bencDictAddDict( d, key, 6 );
546        const int * f = st->peersFrom;
547        tr_bencDictAddInt( tmp, "fromCache",    f[TR_PEER_FROM_RESUME] );
548        tr_bencDictAddInt( tmp, "fromDht",      f[TR_PEER_FROM_DHT] );
549        tr_bencDictAddInt( tmp, "fromIncoming", f[TR_PEER_FROM_INCOMING] );
550        tr_bencDictAddInt( tmp, "fromLtep",     f[TR_PEER_FROM_LTEP] );
551        tr_bencDictAddInt( tmp, "fromPex",      f[TR_PEER_FROM_PEX] );
552        tr_bencDictAddInt( tmp, "fromTracker",  f[TR_PEER_FROM_TRACKER] );
553    }
554    else if( tr_streq( key, keylen, "peersGettingFromUs" ) )
555        tr_bencDictAddInt( d, key, st->peersGettingFromUs );
556    else if( tr_streq( key, keylen, "peersKnown" ) )
557        tr_bencDictAddInt( d, key, st->peersKnown );
558    else if( tr_streq( key, keylen, "peersSendingToUs" ) )
559        tr_bencDictAddInt( d, key, st->peersSendingToUs );
560    else if( tr_streq( key, keylen, "pieces" ) ) {
561        const tr_bitfield * pieces = tr_cpPieceBitfield( &tor->completion );
562        char * str = tr_base64_encode( pieces->bits, pieces->byteCount, NULL );
563        tr_bencDictAddStr( d, key, str!=NULL ? str : "" );
564        tr_free( str );
565    }
566    else if( tr_streq( key, keylen, "pieceCount" ) )
567        tr_bencDictAddInt( d, key, inf->pieceCount );
568    else if( tr_streq( key, keylen, "pieceSize" ) )
569        tr_bencDictAddInt( d, key, inf->pieceSize );
570    else if( tr_streq( key, keylen, "priorities" ) )
571    {
572        tr_file_index_t i;
573        tr_benc *       p = tr_bencDictAddList( d, key, inf->fileCount );
574        for( i = 0; i < inf->fileCount; ++i )
575            tr_bencListAddInt( p, inf->files[i].priority );
576    }
577    else if( tr_streq( key, keylen, "rateDownload" ) )
578        tr_bencDictAddInt( d, key, toSpeedBytes( st->pieceDownloadSpeed_KBps ) );
579    else if( tr_streq( key, keylen, "rateUpload" ) )
580        tr_bencDictAddInt( d, key, toSpeedBytes( st->pieceUploadSpeed_KBps ) );
581    else if( tr_streq( key, keylen, "recheckProgress" ) )
582        tr_bencDictAddReal( d, key, st->recheckProgress );
583    else if( tr_streq( key, keylen, "seedIdleLimit" ) )
584        tr_bencDictAddInt( d, key, tr_torrentGetIdleLimit( tor ) );
585    else if( tr_streq( key, keylen, "seedIdleMode" ) )
586        tr_bencDictAddInt( d, key, tr_torrentGetIdleMode( tor ) );
587    else if( tr_streq( key, keylen, "seedRatioLimit" ) )
588        tr_bencDictAddReal( d, key, tr_torrentGetRatioLimit( tor ) );
589    else if( tr_streq( key, keylen, "seedRatioMode" ) )
590        tr_bencDictAddInt( d, key, tr_torrentGetRatioMode( tor ) );
591    else if( tr_streq( key, keylen, "sizeWhenDone" ) )
592        tr_bencDictAddInt( d, key, st->sizeWhenDone );
593    else if( tr_streq( key, keylen, "startDate" ) )
594        tr_bencDictAddInt( d, key, st->startDate );
595    else if( tr_streq( key, keylen, "status" ) )
596        tr_bencDictAddInt( d, key, st->activity );
597    else if( tr_streq( key, keylen, "secondsDownloading" ) )
598        tr_bencDictAddInt( d, key, st->secondsDownloading );
599    else if( tr_streq( key, keylen, "secondsSeeding" ) )
600        tr_bencDictAddInt( d, key, st->secondsSeeding );
601    else if( tr_streq( key, keylen, "trackers" ) )
602        addTrackers( inf, tr_bencDictAddList( d, key, inf->trackerCount ) );
603    else if( tr_streq( key, keylen, "trackerStats" ) ) {
604        int n;
605        tr_tracker_stat * s = tr_torrentTrackers( tor, &n );
606        addTrackerStats( s, n, tr_bencDictAddList( d, key, n ) );
607        tr_torrentTrackersFree( s, n );
608    }
609    else if( tr_streq( key, keylen, "torrentFile" ) )
610        tr_bencDictAddStr( d, key, inf->torrent );
611    else if( tr_streq( key, keylen, "totalSize" ) )
612        tr_bencDictAddInt( d, key, inf->totalSize );
613    else if( tr_streq( key, keylen, "uploadedEver" ) )
614        tr_bencDictAddInt( d, key, st->uploadedEver );
615    else if( tr_streq( key, keylen, "uploadLimit" ) )
616        tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit_KBps( tor, TR_UP ) );
617    else if( tr_streq( key, keylen, "uploadLimited" ) )
618        tr_bencDictAddBool( d, key, tr_torrentUsesSpeedLimit( tor, TR_UP ) );
619    else if( tr_streq( key, keylen, "uploadRatio" ) )
620        tr_bencDictAddReal( d, key, st->ratio );
621    else if( tr_streq( key, keylen, "wanted" ) )
622    {
623        tr_file_index_t i;
624        tr_benc *       w = tr_bencDictAddList( d, key, inf->fileCount );
625        for( i = 0; i < inf->fileCount; ++i )
626            tr_bencListAddInt( w, inf->files[i].dnd ? 0 : 1 );
627    }
628    else if( tr_streq( key, keylen, "webseeds" ) )
629        addWebseeds( inf, tr_bencDictAddList( d, key, inf->webseedCount ) );
630    else if( tr_streq( key, keylen, "webseedsSendingToUs" ) )
631        tr_bencDictAddInt( d, key, st->webseedsSendingToUs );
632}
633
634static void
635addInfo( const tr_torrent * tor,
636         tr_benc *          d,
637         tr_benc *          fields )
638{
639    int          i;
640    const int    n = tr_bencListSize( fields );
641    const char * str;
642
643    tr_bencInitDict( d, n );
644
645    for( i = 0; i < n; ++i )
646        if( tr_bencGetStr( tr_bencListChild( fields, i ), &str ) )
647            addField( tor, d, str );
648}
649
650static const char*
651torrentGet( tr_session               * session,
652            tr_benc                  * args_in,
653            tr_benc                  * args_out,
654            struct tr_rpc_idle_data  * idle_data UNUSED )
655{
656    int           i, torrentCount;
657    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
658    tr_benc *     list = tr_bencDictAddList( args_out, "torrents", torrentCount );
659    tr_benc *     fields;
660    const char *  msg = NULL;
661    const char *  strVal;
662
663    assert( idle_data == NULL );
664
665    if( tr_bencDictFindStr( args_in, "ids", &strVal ) && !strcmp( strVal, "recently-active" ) ) {
666        int n = 0;
667        tr_benc * d;
668        const time_t now = tr_time( );
669        const int interval = RECENTLY_ACTIVE_SECONDS;
670        tr_benc * removed_out = tr_bencDictAddList( args_out, "removed", 0 );
671        while(( d = tr_bencListChild( &session->removedTorrents, n++ ))) {
672            int64_t intVal;
673            if( tr_bencDictFindInt( d, "date", &intVal ) && ( intVal >= now - interval ) ) {
674                tr_bencDictFindInt( d, "id", &intVal );
675                tr_bencListAddInt( removed_out, intVal );
676            }
677        }
678    }
679
680    if( !tr_bencDictFindList( args_in, "fields", &fields ) )
681        msg = "no fields specified";
682    else for( i = 0; i < torrentCount; ++i )
683        addInfo( torrents[i], tr_bencListAdd( list ), fields );
684
685    tr_free( torrents );
686    return msg;
687}
688
689/***
690****
691***/
692
693static const char*
694setFilePriorities( tr_torrent * tor,
695                   int          priority,
696                   tr_benc *    list )
697{
698    int i;
699    int64_t tmp;
700    int fileCount = 0;
701    const int n = tr_bencListSize( list );
702    const char * errmsg = NULL;
703    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
704
705    if( n )
706    {
707        for( i = 0; i < n; ++i ) {
708            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) {
709                if( 0 <= tmp && tmp < tor->info.fileCount ) {
710                    files[fileCount++] = tmp;
711                } else {
712                    errmsg = "file index out of range";
713                }
714            }
715        }
716    }
717    else /* if empty set, apply to all */
718    {
719        tr_file_index_t t;
720        for( t = 0; t < tor->info.fileCount; ++t )
721            files[fileCount++] = t;
722    }
723
724    if( fileCount )
725        tr_torrentSetFilePriorities( tor, files, fileCount, priority );
726
727    tr_free( files );
728    return errmsg;
729}
730
731static const char*
732setFileDLs( tr_torrent * tor,
733            int          do_download,
734            tr_benc *    list )
735{
736    int i;
737    int64_t tmp;
738    int fileCount = 0;
739    const int n = tr_bencListSize( list );
740    const char * errmsg = NULL;
741    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
742
743    if( n ) /* if argument list, process them */
744    {
745        for( i = 0; i < n; ++i ) {
746            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) {
747                if( 0 <= tmp && tmp < tor->info.fileCount ) {
748                    files[fileCount++] = tmp;
749                } else {
750                    errmsg = "file index out of range";
751                }
752            }
753        }
754    }
755    else /* if empty set, apply to all */
756    {
757        tr_file_index_t t;
758        for( t = 0; t < tor->info.fileCount; ++t )
759            files[fileCount++] = t;
760    }
761
762    if( fileCount )
763        tr_torrentSetFileDLs( tor, files, fileCount, do_download );
764
765    tr_free( files );
766    return errmsg;
767}
768
769static tr_bool
770findAnnounceUrl( const tr_tracker_info * t, int n, const char * url, int * pos )
771{
772    int i;
773    tr_bool found = FALSE;
774
775    for( i=0; i<n; ++i )
776    {
777        if( !strcmp( t[i].announce, url ) )
778        {
779            found = TRUE;
780            if( pos ) *pos = i;
781            break;
782        }
783    }
784
785    return found;
786}
787
788static int
789copyTrackers( tr_tracker_info * tgt, const tr_tracker_info * src, int n )
790{
791    int i;
792    int maxTier = -1;
793
794    for( i=0; i<n; ++i )
795    {
796        tgt[i].tier = src[i].tier;
797        tgt[i].announce = tr_strdup( src[i].announce );
798        maxTier = MAX( maxTier, src[i].tier );
799    }
800
801    return maxTier;
802}
803
804static void
805freeTrackers( tr_tracker_info * trackers, int n )
806{
807    int i;
808
809    for( i=0; i<n; ++i )
810        tr_free( trackers[i].announce );
811
812    tr_free( trackers );
813}
814
815static const char*
816addTrackerUrls( tr_torrent * tor, tr_benc * urls )
817{
818    int i;
819    int n;
820    int tier;
821    tr_benc * val;
822    tr_tracker_info * trackers;
823    tr_bool changed = FALSE;
824    const tr_info * inf = tr_torrentInfo( tor );
825    const char * errmsg = NULL;
826
827    /* make a working copy of the existing announce list */
828    n = inf->trackerCount;
829    trackers = tr_new0( tr_tracker_info, n + tr_bencListSize( urls ) );
830    tier = copyTrackers( trackers, inf->trackers, n );
831
832    /* and add the new ones */
833    i = 0;
834    while(( val = tr_bencListChild( urls, i++ ) ))
835    {
836        const char * announce = NULL;
837
838        if(    tr_bencGetStr( val, &announce )
839            && tr_urlIsValid( announce, -1 )
840            && !findAnnounceUrl( trackers, n, announce, NULL ) )
841        {
842            trackers[n].tier = ++tier; /* add a new tier */
843            trackers[n].announce = tr_strdup( announce );
844            ++n;
845            changed = TRUE;
846        }
847    }
848
849    if( !changed )
850        errmsg = "invalid argument";
851    else if( !tr_torrentSetAnnounceList( tor, trackers, n ) )
852        errmsg = "error setting announce list";
853
854    freeTrackers( trackers, n );
855    return errmsg;
856}
857
858static const char*
859replaceTrackers( tr_torrent * tor, tr_benc * urls )
860{
861    int i;
862    tr_benc * pair[2];
863    tr_tracker_info * trackers;
864    tr_bool changed = FALSE;
865    const tr_info * inf = tr_torrentInfo( tor );
866    const int n = inf->trackerCount;
867    const char * errmsg = NULL;
868
869    /* make a working copy of the existing announce list */
870    trackers = tr_new0( tr_tracker_info, n );
871    copyTrackers( trackers, inf->trackers, n );
872
873    /* make the substitutions... */
874    i = 0;
875    while(((pair[0] = tr_bencListChild(urls,i))) &&
876          ((pair[1] = tr_bencListChild(urls,i+1))))
877    {
878        int64_t pos;
879        const char * newval;
880
881        if(    tr_bencGetInt( pair[0], &pos )
882            && tr_bencGetStr( pair[1], &newval )
883            && tr_urlIsValid( newval, -1 )
884            && pos < n
885            && pos >= 0 )
886        {
887            tr_free( trackers[pos].announce );
888            trackers[pos].announce = tr_strdup( newval );
889            changed = TRUE;
890        }
891
892        i += 2;
893    }
894
895    if( !changed )
896        errmsg = "invalid argument";
897    else if( !tr_torrentSetAnnounceList( tor, trackers, n ) )
898        errmsg = "error setting announce list";
899
900    freeTrackers( trackers, n );
901    return errmsg;
902}
903
904static const char*
905removeTrackers( tr_torrent * tor, tr_benc * ids )
906{
907    int i;
908    int n;
909    int t = 0;
910    int dup = -1;
911    int * tids;
912    tr_benc * val;
913    tr_tracker_info * trackers;
914    tr_bool changed = FALSE;
915    const tr_info * inf = tr_torrentInfo( tor );
916    const char * errmsg = NULL;
917
918    /* make a working copy of the existing announce list */
919    n = inf->trackerCount;
920    tids = tr_new0( int, n );
921    trackers = tr_new0( tr_tracker_info, n );
922    copyTrackers( trackers, inf->trackers, n );
923
924    /* remove the ones specified in the urls list */
925    i = 0;
926    while(( val = tr_bencListChild( ids, i++ )))
927    {
928        int64_t pos;
929
930        if(    tr_bencGetInt( val, &pos )
931            && pos < n
932            && pos >= 0 )
933            tids[t++] = pos;
934    }
935
936    /* sort trackerIds and remove from largest to smallest so there is no need to recacluate array indicies */
937    qsort( tids, t, sizeof(int), compareInt );
938    while( t-- )
939    {
940        /* check for duplicates */
941        if( tids[t] == dup )
942            continue;
943        tr_removeElementFromArray( trackers, tids[t], sizeof( tr_tracker_info ), n-- );
944        dup = tids[t];
945        changed = TRUE;
946    }
947
948    if( !changed )
949        errmsg = "invalid argument";
950    else if( !tr_torrentSetAnnounceList( tor, trackers, n ) )
951        errmsg = "error setting announce list";
952
953    freeTrackers( trackers, n );
954    tr_free( tids );
955    return errmsg;
956}
957
958static const char*
959torrentSet( tr_session               * session,
960            tr_benc                  * args_in,
961            tr_benc                  * args_out UNUSED,
962            struct tr_rpc_idle_data  * idle_data UNUSED )
963{
964    const char * errmsg = NULL;
965    int i, torrentCount;
966    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
967
968    assert( idle_data == NULL );
969
970    for( i = 0; i < torrentCount; ++i )
971    {
972        int64_t      tmp;
973        double       d;
974        tr_benc *    files;
975        tr_benc *    trackers;
976        tr_bool      boolVal;
977        tr_torrent * tor = torrents[i];
978
979        if( tr_bencDictFindInt( args_in, "bandwidthPriority", &tmp ) )
980            if( tr_isPriority( tmp ) )
981                tr_torrentSetPriority( tor, tmp );
982        if( !errmsg && tr_bencDictFindList( args_in, "files-unwanted", &files ) )
983            errmsg = setFileDLs( tor, FALSE, files );
984        if( !errmsg && tr_bencDictFindList( args_in, "files-wanted", &files ) )
985            errmsg = setFileDLs( tor, TRUE, files );
986        if( tr_bencDictFindInt( args_in, "peer-limit", &tmp ) )
987            tr_torrentSetPeerLimit( tor, tmp );
988        if( !errmsg &&  tr_bencDictFindList( args_in, "priority-high", &files ) )
989            errmsg = setFilePriorities( tor, TR_PRI_HIGH, files );
990        if( !errmsg && tr_bencDictFindList( args_in, "priority-low", &files ) )
991            errmsg = setFilePriorities( tor, TR_PRI_LOW, files );
992        if( !errmsg && tr_bencDictFindList( args_in, "priority-normal", &files ) )
993            errmsg = setFilePriorities( tor, TR_PRI_NORMAL, files );
994        if( tr_bencDictFindInt( args_in, "downloadLimit", &tmp ) )
995            tr_torrentSetSpeedLimit_KBps( tor, TR_DOWN, tmp );
996        if( tr_bencDictFindBool( args_in, "downloadLimited", &boolVal ) )
997            tr_torrentUseSpeedLimit( tor, TR_DOWN, boolVal );
998        if( tr_bencDictFindBool( args_in, "honorsSessionLimits", &boolVal ) )
999            tr_torrentUseSessionLimits( tor, boolVal );
1000        if( tr_bencDictFindInt( args_in, "uploadLimit", &tmp ) )
1001            tr_torrentSetSpeedLimit_KBps( tor, TR_UP, tmp );
1002        if( tr_bencDictFindBool( args_in, "uploadLimited", &boolVal ) )
1003            tr_torrentUseSpeedLimit( tor, TR_UP, boolVal );
1004        if( tr_bencDictFindInt( args_in, "seedIdleLimit", &tmp ) )
1005            tr_torrentSetIdleLimit( tor, tmp );
1006        if( tr_bencDictFindInt( args_in, "seedIdleMode", &tmp ) )
1007            tr_torrentSetIdleMode( tor, tmp );
1008        if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
1009            tr_torrentSetRatioLimit( tor, d );
1010        if( tr_bencDictFindInt( args_in, "seedRatioMode", &tmp ) )
1011            tr_torrentSetRatioMode( tor, tmp );
1012        if( !errmsg && tr_bencDictFindList( args_in, "trackerAdd", &trackers ) )
1013            errmsg = addTrackerUrls( tor, trackers );
1014        if( !errmsg && tr_bencDictFindList( args_in, "trackerRemove", &trackers ) )
1015            errmsg = removeTrackers( tor, trackers );
1016        if( !errmsg && tr_bencDictFindList( args_in, "trackerReplace", &trackers ) )
1017            errmsg = replaceTrackers( tor, trackers );
1018        notify( session, TR_RPC_TORRENT_CHANGED, tor );
1019    }
1020
1021    tr_free( torrents );
1022    return errmsg;
1023}
1024
1025static const char*
1026torrentSetLocation( tr_session               * session,
1027                    tr_benc                  * args_in,
1028                    tr_benc                  * args_out UNUSED,
1029                    struct tr_rpc_idle_data  * idle_data UNUSED )
1030{
1031    const char * errmsg = NULL;
1032    const char * location = NULL;
1033
1034    assert( idle_data == NULL );
1035
1036    if( !tr_bencDictFindStr( args_in, "location", &location ) )
1037    {
1038        errmsg = "no location";
1039    }
1040    else
1041    {
1042        tr_bool move = FALSE;
1043        int i, torrentCount;
1044        tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
1045
1046        tr_bencDictFindBool( args_in, "move", &move );
1047
1048        for( i=0; i<torrentCount; ++i )
1049        {
1050            tr_torrent * tor = torrents[i];
1051            tr_torrentSetLocation( tor, location, move, NULL, NULL );
1052            notify( session, TR_RPC_TORRENT_MOVED, tor );
1053        }
1054
1055        tr_free( torrents );
1056    }
1057
1058    return errmsg;
1059}
1060
1061/***
1062****
1063***/
1064
1065static void
1066portTested( tr_session       * session UNUSED,
1067            long               response_code,
1068            const void       * response,
1069            size_t             response_byte_count,
1070            void             * user_data )
1071{
1072    char result[1024];
1073    struct tr_rpc_idle_data * data = user_data;
1074
1075    if( response_code != 200 )
1076    {
1077        tr_snprintf( result, sizeof( result ), "portTested: http error %ld: %s",
1078                     response_code, tr_webGetResponseStr( response_code ) );
1079    }
1080    else /* success */
1081    {
1082        const tr_bool isOpen = response_byte_count && *(char*)response == '1';
1083        tr_bencDictAddBool( data->args_out, "port-is-open", isOpen );
1084        tr_snprintf( result, sizeof( result ), "success" );
1085    }
1086
1087    tr_idle_function_done( data, result );
1088}
1089
1090static const char*
1091portTest( tr_session               * session,
1092          tr_benc                  * args_in UNUSED,
1093          tr_benc                  * args_out UNUSED,
1094          struct tr_rpc_idle_data  * idle_data )
1095{
1096    const int port = tr_sessionGetPeerPort( session );
1097    char * url = tr_strdup_printf( "http://portcheck.transmissionbt.com/%d", port );
1098    tr_webRun( session, url, NULL, portTested, idle_data );
1099    tr_free( url );
1100    return NULL;
1101}
1102
1103/***
1104****
1105***/
1106
1107static void
1108gotNewBlocklist( tr_session       * session,
1109                 long               response_code,
1110                 const void       * response,
1111                 size_t             response_byte_count,
1112                 void             * user_data )
1113{
1114    char result[1024];
1115    struct tr_rpc_idle_data * data = user_data;
1116
1117    if( response_code != 200 )
1118    {
1119        tr_snprintf( result, sizeof( result ), "gotNewBlocklist: http error %ld: %s",
1120                     response_code, tr_webGetResponseStr( response_code ) );
1121    }
1122    else /* successfully fetched the blocklist... */
1123    {
1124        int fd;
1125        const char * configDir = tr_sessionGetConfigDir( session );
1126        char * filename = tr_buildPath( configDir, "blocklist.tmp", NULL );
1127
1128        errno = 0;
1129
1130        if( !errno ) {
1131            fd = tr_open_file_for_writing( filename );
1132            if( fd < 0 )
1133                tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename, tr_strerror( errno ) );
1134        }
1135
1136        if( !errno ) {
1137            const char * buf = response;
1138            size_t buflen = response_byte_count;
1139            while( buflen > 0 ) {
1140                int n = write( fd, buf, buflen );
1141                if( n < 0 ) {
1142                    tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename, tr_strerror( errno ) );
1143                    break;
1144                }
1145                buf += n;
1146                buflen -= n;
1147            }
1148            tr_close_file( fd );
1149        }
1150
1151#ifdef HAVE_ZLIB
1152        if( !errno )
1153        {
1154            char * filename2 = tr_buildPath( configDir, "blocklist.txt.tmp", NULL );
1155            fd = tr_open_file_for_writing( filename2 );
1156            if( fd < 0 )
1157                tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename2, tr_strerror( errno ) );
1158            else {
1159                gzFile gzf = gzopen( filename, "r" );
1160                if( gzf ) {
1161                    const size_t buflen = 1024 * 128; /* 128 KiB buffer */
1162                    uint8_t * buf = tr_valloc( buflen );
1163                    for( ;; ) {
1164                        int n = gzread( gzf, buf, buflen );
1165                        if( n < 0 ) /* error */
1166                            tr_snprintf( result, sizeof( result ), _( "Error reading \"%1$s\": %2$s" ), filename, gzerror( gzf, NULL ) );
1167                        if( n < 1 ) /* error or EOF */
1168                            break;
1169                        if( write( fd, buf, n ) < 0 )
1170                            tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename2, tr_strerror( errno ) );
1171                    }
1172                    tr_free( buf );
1173                    gzclose( gzf );
1174                }
1175                tr_close_file( fd );
1176            }
1177
1178            unlink( filename );
1179            tr_free( filename );
1180            filename = filename2;
1181        }
1182#endif
1183
1184        if( !errno ) {
1185            /* feed it to the session and give the client a response */
1186            const int rule_count = tr_blocklistSetContent( session, filename );
1187            tr_bencDictAddInt( data->args_out, "blocklist-size", rule_count );
1188            tr_snprintf( result, sizeof( result ), "success" );
1189        }
1190
1191        unlink( filename );
1192        tr_free( filename );
1193    }
1194
1195    tr_idle_function_done( data, result );
1196}
1197
1198static const char*
1199blocklistUpdate( tr_session               * session,
1200                 tr_benc                  * args_in UNUSED,
1201                 tr_benc                  * args_out UNUSED,
1202                 struct tr_rpc_idle_data  * idle_data )
1203{
1204    tr_webRun( session, session->blocklist_url, NULL, gotNewBlocklist, idle_data );
1205    return NULL;
1206}
1207
1208/***
1209****
1210***/
1211
1212static void
1213addTorrentImpl( struct tr_rpc_idle_data * data, tr_ctor * ctor )
1214{
1215    int err = 0;
1216    const char * result = NULL;
1217    tr_torrent * tor = tr_torrentNew( ctor, &err );
1218
1219    tr_ctorFree( ctor );
1220
1221    if( tor )
1222    {
1223        tr_benc fields;
1224        tr_bencInitList( &fields, 3 );
1225        tr_bencListAddStr( &fields, "id" );
1226        tr_bencListAddStr( &fields, "name" );
1227        tr_bencListAddStr( &fields, "hashString" );
1228        addInfo( tor, tr_bencDictAdd( data->args_out, "torrent-added" ), &fields );
1229        notify( data->session, TR_RPC_TORRENT_ADDED, tor );
1230        tr_bencFree( &fields );
1231    }
1232    else if( err == TR_PARSE_DUPLICATE )
1233    {
1234        result = "duplicate torrent";
1235    }
1236    else if( err == TR_PARSE_ERR )
1237    {
1238        result = "invalid or corrupt torrent file";
1239    }
1240
1241    tr_idle_function_done( data, result );
1242}
1243
1244
1245struct add_torrent_idle_data
1246{
1247    struct tr_rpc_idle_data * data;
1248    tr_ctor * ctor;
1249};
1250
1251static void
1252gotMetadataFromURL( tr_session       * session UNUSED,
1253                    long               response_code,
1254                    const void       * response,
1255                    size_t             response_byte_count,
1256                    void             * user_data )
1257{
1258    struct add_torrent_idle_data * data = user_data;
1259
1260    dbgmsg( "torrentAdd: HTTP response code was %ld (%s); response length was %zu bytes",
1261            response_code, tr_webGetResponseStr( response_code ), response_byte_count );
1262
1263    if( response_code==200 || response_code==221 ) /* http or ftp success.. */
1264    {
1265        tr_ctorSetMetainfo( data->ctor, response, response_byte_count );
1266        addTorrentImpl( data->data, data->ctor );
1267    }
1268    else
1269    {
1270        char result[1024];
1271        tr_snprintf( result, sizeof( result ), "gotMetadataFromURL: http error %ld: %s",
1272                     response_code, tr_webGetResponseStr( response_code ) );
1273        tr_idle_function_done( data->data, result );
1274    }
1275
1276    tr_free( data );
1277}
1278
1279static tr_bool
1280isCurlURL( const char * filename )
1281{
1282    if( filename == NULL )
1283        return FALSE;
1284
1285    return !strncmp( filename, "ftp://", 6 ) ||
1286           !strncmp( filename, "http://", 7 ) ||
1287           !strncmp( filename, "https://", 8 );
1288}
1289
1290static tr_file_index_t*
1291fileListFromList( tr_benc * list, tr_file_index_t * setmeCount )
1292{
1293    size_t i;
1294    const size_t childCount = tr_bencListSize( list );
1295    tr_file_index_t n = 0;
1296    tr_file_index_t * files = tr_new0( tr_file_index_t, childCount );
1297
1298    for( i=0; i<childCount; ++i ) {
1299        int64_t intVal;
1300        if( tr_bencGetInt( tr_bencListChild( list, i ), &intVal ) )
1301            files[n++] = (tr_file_index_t)intVal;
1302    }
1303
1304    *setmeCount = n;
1305    return files;
1306}
1307
1308static const char*
1309torrentAdd( tr_session               * session,
1310            tr_benc                  * args_in,
1311            tr_benc                  * args_out UNUSED,
1312            struct tr_rpc_idle_data  * idle_data )
1313{
1314    const char * filename = NULL;
1315    const char * metainfo_base64 = NULL;
1316
1317    assert( idle_data != NULL );
1318
1319    tr_bencDictFindStr( args_in, "filename", &filename );
1320    tr_bencDictFindStr( args_in, "metainfo", &metainfo_base64 );
1321    if( !filename && !metainfo_base64 )
1322        return "no filename or metainfo specified";
1323    else
1324    {
1325        int64_t      i;
1326        tr_bool      boolVal;
1327        const char * str;
1328        tr_benc    * l;
1329        tr_ctor    * ctor = tr_ctorNew( session );
1330
1331        /* set the optional arguments */
1332
1333        if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1334            tr_ctorSetDownloadDir( ctor, TR_FORCE, str );
1335
1336        if( tr_bencDictFindBool( args_in, "paused", &boolVal ) )
1337            tr_ctorSetPaused( ctor, TR_FORCE, boolVal );
1338
1339        if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
1340            tr_ctorSetPeerLimit( ctor, TR_FORCE, i );
1341
1342        if( tr_bencDictFindInt( args_in, "bandwidthPriority", &i ) )
1343            tr_ctorSetBandwidthPriority( ctor, i );
1344
1345        if( tr_bencDictFindList( args_in, "files-unwanted", &l ) ) {
1346            tr_file_index_t fileCount;
1347            tr_file_index_t * files = fileListFromList( l, &fileCount );
1348            tr_ctorSetFilesWanted( ctor, files, fileCount, FALSE );
1349            tr_free( files );
1350        }
1351        if( tr_bencDictFindList( args_in, "files-wanted", &l ) ) {
1352            tr_file_index_t fileCount;
1353            tr_file_index_t * files = fileListFromList( l, &fileCount );
1354            tr_ctorSetFilesWanted( ctor, files, fileCount, TRUE );
1355            tr_free( files );
1356        }
1357
1358        if( tr_bencDictFindList( args_in, "priority-low", &l ) ) {
1359            tr_file_index_t fileCount;
1360            tr_file_index_t * files = fileListFromList( l, &fileCount );
1361            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_LOW );
1362            tr_free( files );
1363        }
1364        if( tr_bencDictFindList( args_in, "priority-normal", &l ) ) {
1365            tr_file_index_t fileCount;
1366            tr_file_index_t * files = fileListFromList( l, &fileCount );
1367            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_NORMAL );
1368            tr_free( files );
1369        }
1370        if( tr_bencDictFindList( args_in, "priority-high", &l ) ) {
1371            tr_file_index_t fileCount;
1372            tr_file_index_t * files = fileListFromList( l, &fileCount );
1373            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_HIGH );
1374            tr_free( files );
1375        }
1376
1377        dbgmsg( "torrentAdd: filename is \"%s\"", filename ? filename : "(null)" );
1378
1379        if( isCurlURL( filename ) )
1380        {
1381            struct add_torrent_idle_data * d = tr_new0( struct add_torrent_idle_data, 1 );
1382            d->data = idle_data;
1383            d->ctor = ctor;
1384            tr_webRun( session, filename, NULL, gotMetadataFromURL, d );
1385        }
1386        else
1387        {
1388            char * fname = tr_strstrip( tr_strdup( filename ) );
1389
1390            if( fname == NULL )
1391            {
1392                int len;
1393                char * metainfo = tr_base64_decode( metainfo_base64, -1, &len );
1394                tr_ctorSetMetainfo( ctor, (uint8_t*)metainfo, len );
1395                tr_free( metainfo );
1396            }
1397            else if( !strncmp( fname, "magnet:?", 8 ) )
1398            {
1399                tr_ctorSetMetainfoFromMagnetLink( ctor, fname );
1400            }
1401            else
1402            {
1403                tr_ctorSetMetainfoFromFile( ctor, fname );
1404            }
1405
1406            addTorrentImpl( idle_data, ctor );
1407
1408            tr_free( fname );
1409        }
1410
1411    }
1412
1413    return NULL;
1414}
1415
1416/***
1417****
1418***/
1419
1420static const char*
1421sessionSet( tr_session               * session,
1422            tr_benc                  * args_in,
1423            tr_benc                  * args_out UNUSED,
1424            struct tr_rpc_idle_data  * idle_data UNUSED )
1425{
1426    int64_t      i;
1427    double       d;
1428    tr_bool      boolVal;
1429    const char * str;
1430
1431    assert( idle_data == NULL );
1432
1433    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, &i ) )
1434        tr_sessionSetCacheLimit_MB( session, i );
1435    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_UP_KBps, &i ) )
1436        tr_sessionSetAltSpeed_KBps( session, TR_UP, i );
1437    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, &i ) )
1438        tr_sessionSetAltSpeed_KBps( session, TR_DOWN, i );
1439    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_ENABLED, &boolVal ) )
1440        tr_sessionUseAltSpeed( session, boolVal );
1441    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &i ) )
1442        tr_sessionSetAltSpeedBegin( session, i );
1443    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_END, &i ) )
1444        tr_sessionSetAltSpeedEnd( session, i );
1445    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &i ) )
1446        tr_sessionSetAltSpeedDay( session, i );
1447    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &boolVal ) )
1448        tr_sessionUseAltSpeedTime( session, boolVal );
1449    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_BLOCKLIST_ENABLED, &boolVal ) )
1450        tr_blocklistSetEnabled( session, boolVal );
1451    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_BLOCKLIST_URL, &str ) )
1452        tr_blocklistSetURL( session, str );
1453    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1454        tr_sessionSetDownloadDir( session, str );
1455    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_INCOMPLETE_DIR, &str ) )
1456        tr_sessionSetIncompleteDir( session, str );
1457    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, &boolVal ) )
1458        tr_sessionSetIncompleteDirEnabled( session, boolVal );
1459    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &i ) )
1460        tr_sessionSetPeerLimit( session, i );
1461    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i ) )
1462        tr_sessionSetPeerLimitPerTorrent( session, i );
1463    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) )
1464        tr_sessionSetPexEnabled( session, boolVal );
1465    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DHT_ENABLED, &boolVal ) )
1466        tr_sessionSetDHTEnabled( session, boolVal );
1467    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_LPD_ENABLED, &boolVal ) )
1468        tr_sessionSetLPDEnabled( session, boolVal );
1469    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, &boolVal ) )
1470        tr_sessionSetPeerPortRandomOnStart( session, boolVal );
1471    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_PORT, &i ) )
1472        tr_sessionSetPeerPort( session, i );
1473    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) )
1474        tr_sessionSetPortForwardingEnabled( session, boolVal );
1475    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_RENAME_PARTIAL_FILES, &boolVal ) )
1476        tr_sessionSetIncompleteFileNamingEnabled( session, boolVal );
1477    if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
1478        tr_sessionSetRatioLimit( session, d );
1479    if( tr_bencDictFindBool( args_in, "seedRatioLimited", &boolVal ) )
1480        tr_sessionSetRatioLimited( session, boolVal );
1481    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_IDLE_LIMIT, &i ) )
1482        tr_sessionSetIdleLimit( session, i );
1483    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, &boolVal ) )
1484        tr_sessionSetIdleLimited( session, boolVal );
1485    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_START, &boolVal ) )
1486        tr_sessionSetPaused( session, !boolVal );
1487    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, &str ) )
1488        tr_sessionSetTorrentDoneScript( session, str );
1489    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, &boolVal ) )
1490        tr_sessionSetTorrentDoneScriptEnabled( session, boolVal );
1491    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_TRASH_ORIGINAL, &boolVal ) )
1492        tr_sessionSetDeleteSource( session, boolVal );
1493    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_DSPEED_KBps, &i ) )
1494        tr_sessionSetSpeedLimit_KBps( session, TR_DOWN, i );
1495    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DSPEED_ENABLED, &boolVal ) )
1496        tr_sessionLimitSpeed( session, TR_DOWN, boolVal );
1497    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_USPEED_KBps, &i ) )
1498        tr_sessionSetSpeedLimit_KBps( session, TR_UP, i );
1499    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_USPEED_ENABLED, &boolVal ) )
1500        tr_sessionLimitSpeed( session, TR_UP, boolVal );
1501    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_ENCRYPTION, &str ) ) {
1502        if( !strcmp( str, "required" ) )
1503            tr_sessionSetEncryption( session, TR_ENCRYPTION_REQUIRED );
1504        else if( !strcmp( str, "tolerated" ) )
1505            tr_sessionSetEncryption( session, TR_CLEAR_PREFERRED );
1506        else
1507            tr_sessionSetEncryption( session, TR_ENCRYPTION_PREFERRED );
1508    }
1509
1510    notify( session, TR_RPC_SESSION_CHANGED, NULL );
1511
1512    return NULL;
1513}
1514
1515static const char*
1516sessionStats( tr_session               * session,
1517              tr_benc                  * args_in UNUSED,
1518              tr_benc                  * args_out,
1519              struct tr_rpc_idle_data  * idle_data UNUSED )
1520{
1521    int running = 0;
1522    int total = 0;
1523    tr_benc * d;
1524    tr_session_stats currentStats = { 0.0f, 0, 0, 0, 0, 0 };
1525    tr_session_stats cumulativeStats = { 0.0f, 0, 0, 0, 0, 0 };
1526    tr_torrent * tor = NULL;
1527
1528    assert( idle_data == NULL );
1529
1530    while(( tor = tr_torrentNext( session, tor ))) {
1531        ++total;
1532        if( tor->isRunning )
1533            ++running;
1534    }
1535
1536    tr_sessionGetStats( session, &currentStats );
1537    tr_sessionGetCumulativeStats( session, &cumulativeStats );
1538
1539    tr_bencDictAddInt ( args_out, "activeTorrentCount", running );
1540    tr_bencDictAddReal( args_out, "downloadSpeed", tr_sessionGetPieceSpeed_Bps( session, TR_DOWN ) );
1541    tr_bencDictAddInt ( args_out, "pausedTorrentCount", total - running );
1542    tr_bencDictAddInt ( args_out, "torrentCount", total );
1543    tr_bencDictAddReal( args_out, "uploadSpeed", tr_sessionGetPieceSpeed_Bps( session, TR_UP ) );
1544
1545    d = tr_bencDictAddDict( args_out, "cumulative-stats", 5 );
1546    tr_bencDictAddInt( d, "downloadedBytes", cumulativeStats.downloadedBytes );
1547    tr_bencDictAddInt( d, "filesAdded", cumulativeStats.filesAdded );
1548    tr_bencDictAddInt( d, "secondsActive", cumulativeStats.secondsActive );
1549    tr_bencDictAddInt( d, "sessionCount", cumulativeStats.sessionCount );
1550    tr_bencDictAddInt( d, "uploadedBytes", cumulativeStats.uploadedBytes );
1551
1552    d = tr_bencDictAddDict( args_out, "current-stats", 5 );
1553    tr_bencDictAddInt( d, "downloadedBytes", currentStats.downloadedBytes );
1554    tr_bencDictAddInt( d, "filesAdded", currentStats.filesAdded );
1555    tr_bencDictAddInt( d, "secondsActive", currentStats.secondsActive );
1556    tr_bencDictAddInt( d, "sessionCount", currentStats.sessionCount );
1557    tr_bencDictAddInt( d, "uploadedBytes", currentStats.uploadedBytes );
1558
1559    return NULL;
1560}
1561
1562static const char*
1563sessionGet( tr_session               * s,
1564            tr_benc                  * args_in UNUSED,
1565            tr_benc                  * args_out,
1566            struct tr_rpc_idle_data  * idle_data UNUSED )
1567{
1568    const char * str;
1569    tr_benc * d = args_out;
1570
1571    assert( idle_data == NULL );
1572    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP_KBps, tr_sessionGetAltSpeed_KBps(s,TR_UP) );
1573    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, tr_sessionGetAltSpeed_KBps(s,TR_DOWN) );
1574    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED, tr_sessionUsesAltSpeed(s) );
1575    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, tr_sessionGetAltSpeedBegin(s) );
1576    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,tr_sessionGetAltSpeedEnd(s) );
1577    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,tr_sessionGetAltSpeedDay(s) );
1578    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, tr_sessionUsesAltSpeedTime(s) );
1579    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, tr_blocklistIsEnabled( s ) );
1580    tr_bencDictAddStr ( d, TR_PREFS_KEY_BLOCKLIST_URL, tr_blocklistGetURL( s ) );
1581    tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, tr_sessionGetCacheLimit_MB( s ) );
1582    tr_bencDictAddInt ( d, "blocklist-size", tr_blocklistGetRuleCount( s ) );
1583    tr_bencDictAddStr ( d, "config-dir", tr_sessionGetConfigDir( s ) );
1584    tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR, tr_sessionGetDownloadDir( s ) );
1585    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, tr_sessionGetPeerLimit( s ) );
1586    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT, tr_sessionGetPeerLimitPerTorrent( s ) );
1587    tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR, tr_sessionGetIncompleteDir( s ) );
1588    tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, tr_sessionIsIncompleteDirEnabled( s ) );
1589    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED, tr_sessionIsPexEnabled( s ) );
1590    tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED, tr_sessionIsDHTEnabled( s ) );
1591    tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED, tr_sessionIsLPDEnabled( s ) );
1592    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT, tr_sessionGetPeerPort( s ) );
1593    tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, tr_sessionGetPeerPortRandomOnStart( s ) );
1594    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) );
1595    tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES, tr_sessionIsIncompleteFileNamingEnabled( s ) );
1596    tr_bencDictAddInt ( d, "rpc-version", RPC_VERSION );
1597    tr_bencDictAddInt ( d, "rpc-version-minimum", RPC_VERSION_MIN );
1598    tr_bencDictAddReal( d, "seedRatioLimit", tr_sessionGetRatioLimit( s ) );
1599    tr_bencDictAddBool( d, "seedRatioLimited", tr_sessionIsRatioLimited( s ) );
1600    tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT, tr_sessionGetIdleLimit( s ) );
1601    tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, tr_sessionIsIdleLimited( s ) );
1602    tr_bencDictAddBool( d, TR_PREFS_KEY_START, !tr_sessionGetPaused( s ) );
1603    tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL, tr_sessionGetDeleteSource( s ) );
1604    tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_UP ) );
1605    tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_UP ) );
1606    tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_DOWN ) );
1607    tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_DOWN ) );
1608    tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, tr_sessionGetTorrentDoneScript( s ) );
1609    tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, tr_sessionIsTorrentDoneScriptEnabled( s ) );
1610    tr_formatter_get_units( tr_bencDictAddDict( d, "units", 0 ) );
1611    tr_bencDictAddStr ( d, "version", LONG_VERSION_STRING );
1612    switch( tr_sessionGetEncryption( s ) ) {
1613        case TR_CLEAR_PREFERRED: str = "tolerated"; break;
1614        case TR_ENCRYPTION_REQUIRED: str = "required"; break;
1615        default: str = "preferred"; break;
1616    }
1617    tr_bencDictAddStr( d, TR_PREFS_KEY_ENCRYPTION, str );
1618
1619    return NULL;
1620}
1621
1622/***
1623****
1624***/
1625
1626typedef const char* ( *handler )( tr_session*, tr_benc*, tr_benc*, struct tr_rpc_idle_data * );
1627
1628static struct method
1629{
1630    const char *  name;
1631    tr_bool       immediate;
1632    handler       func;
1633}
1634methods[] =
1635{
1636    { "port-test",             FALSE, portTest            },
1637    { "blocklist-update",      FALSE, blocklistUpdate     },
1638    { "session-get",           TRUE,  sessionGet          },
1639    { "session-set",           TRUE,  sessionSet          },
1640    { "session-stats",         TRUE,  sessionStats        },
1641    { "torrent-add",           FALSE, torrentAdd          },
1642    { "torrent-get",           TRUE,  torrentGet          },
1643    { "torrent-remove",        TRUE,  torrentRemove       },
1644    { "torrent-set",           TRUE,  torrentSet          },
1645    { "torrent-set-location",  TRUE,  torrentSetLocation  },
1646    { "torrent-start",         TRUE,  torrentStart        },
1647    { "torrent-stop",          TRUE,  torrentStop         },
1648    { "torrent-verify",        TRUE,  torrentVerify       },
1649    { "torrent-reannounce",    TRUE,  torrentReannounce   }
1650};
1651
1652static void
1653noop_response_callback( tr_session       * session UNUSED,
1654                        struct evbuffer  * response UNUSED,
1655                        void             * user_data UNUSED )
1656{
1657}
1658
1659static void
1660request_exec( tr_session             * session,
1661              tr_benc                * request,
1662              tr_rpc_response_func     callback,
1663              void                   * callback_user_data )
1664{
1665    int i;
1666    const char * str;
1667    tr_benc * args_in = tr_bencDictFind( request, "arguments" );
1668    const char * result = NULL;
1669
1670    if( callback == NULL )
1671        callback = noop_response_callback;
1672
1673    /* parse the request */
1674    if( !tr_bencDictFindStr( request, "method", &str ) )
1675        result = "no method name";
1676    else {
1677        const int n = TR_N_ELEMENTS( methods );
1678        for( i = 0; i < n; ++i )
1679            if( !strcmp( str, methods[i].name ) )
1680                break;
1681        if( i ==n )
1682            result = "method name not recognized";
1683    }
1684
1685    /* if we couldn't figure out which method to use, return an error */
1686    if( result != NULL )
1687    {
1688        int64_t tag;
1689        tr_benc response;
1690        struct evbuffer * buf = evbuffer_new( );
1691
1692        tr_bencInitDict( &response, 3 );
1693        tr_bencDictAddDict( &response, "arguments", 0 );
1694        tr_bencDictAddStr( &response, "result", result );
1695        if( tr_bencDictFindInt( request, "tag", &tag ) )
1696            tr_bencDictAddInt( &response, "tag", tag );
1697        tr_bencToBuf( &response, TR_FMT_JSON_LEAN, buf );
1698        (*callback)( session, buf, callback_user_data );
1699
1700        evbuffer_free( buf );
1701        tr_bencFree( &response );
1702    }
1703    else if( methods[i].immediate )
1704    {
1705        int64_t tag;
1706        tr_benc response;
1707        tr_benc * args_out;
1708        struct evbuffer * buf = evbuffer_new( );
1709
1710        tr_bencInitDict( &response, 3 );
1711        args_out = tr_bencDictAddDict( &response, "arguments", 0 );
1712        result = (*methods[i].func)( session, args_in, args_out, NULL );
1713        if( result == NULL )
1714            result = "success";
1715        tr_bencDictAddStr( &response, "result", result );
1716        if( tr_bencDictFindInt( request, "tag", &tag ) )
1717            tr_bencDictAddInt( &response, "tag", tag );
1718        tr_bencToBuf( &response, TR_FMT_JSON_LEAN, buf );
1719        (*callback)( session, buf, callback_user_data );
1720
1721        evbuffer_free( buf );
1722        tr_bencFree( &response );
1723    }
1724    else
1725    {
1726        int64_t tag;
1727        struct tr_rpc_idle_data * data = tr_new0( struct tr_rpc_idle_data, 1 );
1728        data->session = session;
1729        data->response = tr_new0( tr_benc, 1 );
1730        tr_bencInitDict( data->response, 3 );
1731        if( tr_bencDictFindInt( request, "tag", &tag ) )
1732            tr_bencDictAddInt( data->response, "tag", tag );
1733        data->args_out = tr_bencDictAddDict( data->response, "arguments", 0 );
1734        data->callback = callback;
1735        data->callback_user_data = callback_user_data;
1736        (*methods[i].func)( session, args_in, data->args_out, data );
1737    }
1738}
1739
1740void
1741tr_rpc_request_exec_json( tr_session            * session,
1742                          const void            * request_json,
1743                          int                     request_len,
1744                          tr_rpc_response_func    callback,
1745                          void                  * callback_user_data )
1746{
1747    tr_benc top;
1748    int have_content;
1749
1750    if( request_len < 0 )
1751        request_len = strlen( request_json );
1752
1753    have_content = !tr_jsonParse( "rpc", request_json, request_len, &top, NULL );
1754    request_exec( session, have_content ? &top : NULL, callback, callback_user_data );
1755
1756    if( have_content )
1757        tr_bencFree( &top );
1758}
1759
1760/**
1761 * Munge the URI into a usable form.
1762 *
1763 * We have very loose typing on this to make the URIs as simple as possible:
1764 * - anything not a 'tag' or 'method' is automatically in 'arguments'
1765 * - values that are all-digits are numbers
1766 * - values that are all-digits or commas are number lists
1767 * - all other values are strings
1768 */
1769void
1770tr_rpc_parse_list_str( tr_benc     * setme,
1771                       const char  * str,
1772                       int           len )
1773
1774{
1775    int valueCount;
1776    int * values = tr_parseNumberRange( str, len, &valueCount );
1777
1778    if( valueCount == 0 )
1779        tr_bencInitStr( setme, str, len );
1780    else if( valueCount == 1 )
1781        tr_bencInitInt( setme, values[0] );
1782    else {
1783        int i;
1784        tr_bencInitList( setme, valueCount );
1785        for( i=0; i<valueCount; ++i )
1786            tr_bencListAddInt( setme, values[i] );
1787    }
1788
1789    tr_free( values );
1790}
1791
1792void
1793tr_rpc_request_exec_uri( tr_session           * session,
1794                         const void           * request_uri,
1795                         int                    request_len,
1796                         tr_rpc_response_func   callback,
1797                         void                 * callback_user_data )
1798{
1799    tr_benc      top, * args;
1800    char *       request = tr_strndup( request_uri, request_len );
1801    const char * pch;
1802
1803    tr_bencInitDict( &top, 3 );
1804    args = tr_bencDictAddDict( &top, "arguments", 0 );
1805
1806    pch = strchr( request, '?' );
1807    if( !pch ) pch = request;
1808    while( pch )
1809    {
1810        const char * delim = strchr( pch, '=' );
1811        const char * next = strchr( pch, '&' );
1812        if( delim )
1813        {
1814            char *    key = tr_strndup( pch, delim - pch );
1815            int       isArg = strcmp( key, "method" ) && strcmp( key, "tag" );
1816            tr_benc * parent = isArg ? args : &top;
1817            tr_rpc_parse_list_str( tr_bencDictAdd( parent, key ),
1818                                  delim + 1,
1819                                  next ? (size_t)(
1820                                       next -
1821                                      ( delim + 1 ) ) : strlen( delim + 1 ) );
1822            tr_free( key );
1823        }
1824        pch = next ? next + 1 : NULL;
1825    }
1826
1827    request_exec( session, &top, callback, callback_user_data );
1828
1829    /* cleanup */
1830    tr_bencFree( &top );
1831    tr_free( request );
1832}
Note: See TracBrowser for help on using the repository browser.