source: trunk/libtransmission/rpcimpl.c @ 11709

Last change on this file since 11709 was 11709, checked in by jordan, 11 years ago

Update the copyright year in the source code comments.

The Berne Convention says that the copyright year is moot, so instead of adding another year to each file as in previous years, I've removed the year altogether from the source code comments in libtransmission, gtk, qt, utils, daemon, and cli.

Juliusz's copyright notice in tr-dht and Johannes' copyright notice in tr-lpd have been left alone; it didn't seem appropriate to modify them.

  • Property svn:keywords set to Date Rev Author Id
File size: 65.1 KB
Line 
1/*
2 * This file Copyright (C) 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 11709 2011-01-19 13:48:47Z jordan $
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, "download-dir-free-space",  tr_sessionGetDownloadDirFreeSpace( s ) );
1586    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, tr_sessionGetPeerLimit( s ) );
1587    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT, tr_sessionGetPeerLimitPerTorrent( s ) );
1588    tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR, tr_sessionGetIncompleteDir( s ) );
1589    tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, tr_sessionIsIncompleteDirEnabled( s ) );
1590    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED, tr_sessionIsPexEnabled( s ) );
1591    tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED, tr_sessionIsDHTEnabled( s ) );
1592    tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED, tr_sessionIsLPDEnabled( s ) );
1593    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT, tr_sessionGetPeerPort( s ) );
1594    tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, tr_sessionGetPeerPortRandomOnStart( s ) );
1595    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) );
1596    tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES, tr_sessionIsIncompleteFileNamingEnabled( s ) );
1597    tr_bencDictAddInt ( d, "rpc-version", RPC_VERSION );
1598    tr_bencDictAddInt ( d, "rpc-version-minimum", RPC_VERSION_MIN );
1599    tr_bencDictAddReal( d, "seedRatioLimit", tr_sessionGetRatioLimit( s ) );
1600    tr_bencDictAddBool( d, "seedRatioLimited", tr_sessionIsRatioLimited( s ) );
1601    tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT, tr_sessionGetIdleLimit( s ) );
1602    tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, tr_sessionIsIdleLimited( s ) );
1603    tr_bencDictAddBool( d, TR_PREFS_KEY_START, !tr_sessionGetPaused( s ) );
1604    tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL, tr_sessionGetDeleteSource( s ) );
1605    tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_UP ) );
1606    tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_UP ) );
1607    tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_DOWN ) );
1608    tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_DOWN ) );
1609    tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, tr_sessionGetTorrentDoneScript( s ) );
1610    tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, tr_sessionIsTorrentDoneScriptEnabled( s ) );
1611    tr_formatter_get_units( tr_bencDictAddDict( d, "units", 0 ) );
1612    tr_bencDictAddStr ( d, "version", LONG_VERSION_STRING );
1613    switch( tr_sessionGetEncryption( s ) ) {
1614        case TR_CLEAR_PREFERRED: str = "tolerated"; break;
1615        case TR_ENCRYPTION_REQUIRED: str = "required"; break;
1616        default: str = "preferred"; break;
1617    }
1618    tr_bencDictAddStr( d, TR_PREFS_KEY_ENCRYPTION, str );
1619   
1620    return NULL;
1621}
1622
1623/***
1624****
1625***/
1626
1627static const char*
1628sessionClose( tr_session               * session,
1629              tr_benc                  * args_in UNUSED,
1630              tr_benc                  * args_out UNUSED,
1631              struct tr_rpc_idle_data  * idle_data UNUSED )
1632{
1633    notify( session, TR_RPC_SESSION_CLOSE, NULL );
1634    return NULL;
1635}
1636
1637/***
1638****
1639***/
1640
1641typedef const char* ( *handler )( tr_session*, tr_benc*, tr_benc*, struct tr_rpc_idle_data * );
1642
1643static struct method
1644{
1645    const char *  name;
1646    tr_bool       immediate;
1647    handler       func;
1648}
1649methods[] =
1650{
1651    { "port-test",             FALSE, portTest            },
1652    { "blocklist-update",      FALSE, blocklistUpdate     },
1653    { "session-close",         TRUE,  sessionClose        },
1654    { "session-get",           TRUE,  sessionGet          },
1655    { "session-set",           TRUE,  sessionSet          },
1656    { "session-stats",         TRUE,  sessionStats        },
1657    { "torrent-add",           FALSE, torrentAdd          },
1658    { "torrent-get",           TRUE,  torrentGet          },
1659    { "torrent-remove",        TRUE,  torrentRemove       },
1660    { "torrent-set",           TRUE,  torrentSet          },
1661    { "torrent-set-location",  TRUE,  torrentSetLocation  },
1662    { "torrent-start",         TRUE,  torrentStart        },
1663    { "torrent-stop",          TRUE,  torrentStop         },
1664    { "torrent-verify",        TRUE,  torrentVerify       },
1665    { "torrent-reannounce",    TRUE,  torrentReannounce   }
1666};
1667
1668static void
1669noop_response_callback( tr_session       * session UNUSED,
1670                        struct evbuffer  * response UNUSED,
1671                        void             * user_data UNUSED )
1672{
1673}
1674
1675static void
1676request_exec( tr_session             * session,
1677              tr_benc                * request,
1678              tr_rpc_response_func     callback,
1679              void                   * callback_user_data )
1680{
1681    int i;
1682    const char * str;
1683    tr_benc * args_in = tr_bencDictFind( request, "arguments" );
1684    const char * result = NULL;
1685
1686    if( callback == NULL )
1687        callback = noop_response_callback;
1688
1689    /* parse the request */
1690    if( !tr_bencDictFindStr( request, "method", &str ) )
1691        result = "no method name";
1692    else {
1693        const int n = TR_N_ELEMENTS( methods );
1694        for( i = 0; i < n; ++i )
1695            if( !strcmp( str, methods[i].name ) )
1696                break;
1697        if( i ==n )
1698            result = "method name not recognized";
1699    }
1700
1701    /* if we couldn't figure out which method to use, return an error */
1702    if( result != NULL )
1703    {
1704        int64_t tag;
1705        tr_benc response;
1706        struct evbuffer * buf = evbuffer_new( );
1707
1708        tr_bencInitDict( &response, 3 );
1709        tr_bencDictAddDict( &response, "arguments", 0 );
1710        tr_bencDictAddStr( &response, "result", result );
1711        if( tr_bencDictFindInt( request, "tag", &tag ) )
1712            tr_bencDictAddInt( &response, "tag", tag );
1713        tr_bencToBuf( &response, TR_FMT_JSON_LEAN, buf );
1714        (*callback)( session, buf, callback_user_data );
1715
1716        evbuffer_free( buf );
1717        tr_bencFree( &response );
1718    }
1719    else if( methods[i].immediate )
1720    {
1721        int64_t tag;
1722        tr_benc response;
1723        tr_benc * args_out;
1724        struct evbuffer * buf = evbuffer_new( );
1725
1726        tr_bencInitDict( &response, 3 );
1727        args_out = tr_bencDictAddDict( &response, "arguments", 0 );
1728        result = (*methods[i].func)( session, args_in, args_out, NULL );
1729        if( result == NULL )
1730            result = "success";
1731        tr_bencDictAddStr( &response, "result", result );
1732        if( tr_bencDictFindInt( request, "tag", &tag ) )
1733            tr_bencDictAddInt( &response, "tag", tag );
1734        tr_bencToBuf( &response, TR_FMT_JSON_LEAN, buf );
1735        (*callback)( session, buf, callback_user_data );
1736
1737        evbuffer_free( buf );
1738        tr_bencFree( &response );
1739    }
1740    else
1741    {
1742        int64_t tag;
1743        struct tr_rpc_idle_data * data = tr_new0( struct tr_rpc_idle_data, 1 );
1744        data->session = session;
1745        data->response = tr_new0( tr_benc, 1 );
1746        tr_bencInitDict( data->response, 3 );
1747        if( tr_bencDictFindInt( request, "tag", &tag ) )
1748            tr_bencDictAddInt( data->response, "tag", tag );
1749        data->args_out = tr_bencDictAddDict( data->response, "arguments", 0 );
1750        data->callback = callback;
1751        data->callback_user_data = callback_user_data;
1752        (*methods[i].func)( session, args_in, data->args_out, data );
1753    }
1754}
1755
1756void
1757tr_rpc_request_exec_json( tr_session            * session,
1758                          const void            * request_json,
1759                          int                     request_len,
1760                          tr_rpc_response_func    callback,
1761                          void                  * callback_user_data )
1762{
1763    tr_benc top;
1764    int have_content;
1765
1766    if( request_len < 0 )
1767        request_len = strlen( request_json );
1768
1769    have_content = !tr_jsonParse( "rpc", request_json, request_len, &top, NULL );
1770    request_exec( session, have_content ? &top : NULL, callback, callback_user_data );
1771
1772    if( have_content )
1773        tr_bencFree( &top );
1774}
1775
1776/**
1777 * Munge the URI into a usable form.
1778 *
1779 * We have very loose typing on this to make the URIs as simple as possible:
1780 * - anything not a 'tag' or 'method' is automatically in 'arguments'
1781 * - values that are all-digits are numbers
1782 * - values that are all-digits or commas are number lists
1783 * - all other values are strings
1784 */
1785void
1786tr_rpc_parse_list_str( tr_benc     * setme,
1787                       const char  * str,
1788                       int           len )
1789
1790{
1791    int valueCount;
1792    int * values = tr_parseNumberRange( str, len, &valueCount );
1793
1794    if( valueCount == 0 )
1795        tr_bencInitStr( setme, str, len );
1796    else if( valueCount == 1 )
1797        tr_bencInitInt( setme, values[0] );
1798    else {
1799        int i;
1800        tr_bencInitList( setme, valueCount );
1801        for( i=0; i<valueCount; ++i )
1802            tr_bencListAddInt( setme, values[i] );
1803    }
1804
1805    tr_free( values );
1806}
1807
1808void
1809tr_rpc_request_exec_uri( tr_session           * session,
1810                         const void           * request_uri,
1811                         int                    request_len,
1812                         tr_rpc_response_func   callback,
1813                         void                 * callback_user_data )
1814{
1815    tr_benc      top, * args;
1816    char *       request = tr_strndup( request_uri, request_len );
1817    const char * pch;
1818
1819    tr_bencInitDict( &top, 3 );
1820    args = tr_bencDictAddDict( &top, "arguments", 0 );
1821
1822    pch = strchr( request, '?' );
1823    if( !pch ) pch = request;
1824    while( pch )
1825    {
1826        const char * delim = strchr( pch, '=' );
1827        const char * next = strchr( pch, '&' );
1828        if( delim )
1829        {
1830            char *    key = tr_strndup( pch, delim - pch );
1831            int       isArg = strcmp( key, "method" ) && strcmp( key, "tag" );
1832            tr_benc * parent = isArg ? args : &top;
1833            tr_rpc_parse_list_str( tr_bencDictAdd( parent, key ),
1834                                  delim + 1,
1835                                  next ? (size_t)(
1836                                       next -
1837                                      ( delim + 1 ) ) : strlen( delim + 1 ) );
1838            tr_free( key );
1839        }
1840        pch = next ? next + 1 : NULL;
1841    }
1842
1843    request_exec( session, &top, callback, callback_user_data );
1844
1845    /* cleanup */
1846    tr_bencFree( &top );
1847    tr_free( request );
1848}
Note: See TracBrowser for help on using the repository browser.