source: trunk/libtransmission/rpcimpl.c @ 12248

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

(trunk libT) break the mac build and introduce new crashes.

This is partially to address #4145 "Downloads stuck at 100%" by refactoring the bitset, bitfield, and tr_completion; however, the ripple effect is larger than usual so things may get worse in the short term before getting better.

livings124: to fix the mac build, remove bitset.[ch] from xcode

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