source: trunk/libtransmission/rpcimpl.c @ 12494

Last change on this file since 12494 was 12494, checked in by jordan, 5 years ago

(trunk libT) #4101 "RPC method blocklist-update is broken" -- add debugging error messages for cases where gzopen/gzread fails

  • Property svn:keywords set to Date Rev Author Id
File size: 66.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 12494 2011-06-10 05:34:16Z 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;
98
99    if( result == NULL )
100        result = "success";
101    tr_bencDictAddStr( data->response, "result", result );
102
103    buf = tr_bencToBuf( data->response, TR_FMT_JSON_LEAN );
104    (*data->callback)( data->session, buf, data->callback_user_data );
105    evbuffer_free( buf );
106
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 * const tor,
467          const tr_info    * const inf,
468          const tr_stat    * const st,
469          tr_benc          * const d,
470          const char       * const key )
471{
472    const size_t keylen = strlen( key );
473
474    if( tr_streq( key, keylen, "activityDate" ) )
475        tr_bencDictAddInt( d, key, st->activityDate );
476    else if( tr_streq( key, keylen, "addedDate" ) )
477        tr_bencDictAddInt( d, key, st->addedDate );
478    else if( tr_streq( key, keylen, "bandwidthPriority" ) )
479        tr_bencDictAddInt( d, key, tr_torrentGetPriority( tor ) );
480    else if( tr_streq( key, keylen, "comment" ) )
481        tr_bencDictAddStr( d, key, inf->comment ? inf->comment : "" );
482    else if( tr_streq( key, keylen, "corruptEver" ) )
483        tr_bencDictAddInt( d, key, st->corruptEver );
484    else if( tr_streq( key, keylen, "creator" ) )
485        tr_bencDictAddStr( d, key, inf->creator ? inf->creator : "" );
486    else if( tr_streq( key, keylen, "dateCreated" ) )
487        tr_bencDictAddInt( d, key, inf->dateCreated );
488    else if( tr_streq( key, keylen, "desiredAvailable" ) )
489        tr_bencDictAddInt( d, key, st->desiredAvailable );
490    else if( tr_streq( key, keylen, "doneDate" ) )
491        tr_bencDictAddInt( d, key, st->doneDate );
492    else if( tr_streq( key, keylen, "downloadDir" ) )
493        tr_bencDictAddStr( d, key, tr_torrentGetDownloadDir( tor ) );
494    else if( tr_streq( key, keylen, "downloadedEver" ) )
495        tr_bencDictAddInt( d, key, st->downloadedEver );
496    else if( tr_streq( key, keylen, "downloadLimit" ) )
497        tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit_KBps( tor, TR_DOWN ) );
498    else if( tr_streq( key, keylen, "downloadLimited" ) )
499        tr_bencDictAddBool( d, key, tr_torrentUsesSpeedLimit( tor, TR_DOWN ) );
500    else if( tr_streq( key, keylen, "error" ) )
501        tr_bencDictAddInt( d, key, st->error );
502    else if( tr_streq( key, keylen, "errorString" ) )
503        tr_bencDictAddStr( d, key, st->errorString );
504    else if( tr_streq( key, keylen, "eta" ) )
505        tr_bencDictAddInt( d, key, st->eta );
506    else if( tr_streq( key, keylen, "files" ) )
507        addFiles( tor, tr_bencDictAddList( d, key, inf->fileCount ) );
508    else if( tr_streq( key, keylen, "fileStats" ) )
509        addFileStats( tor, tr_bencDictAddList( d, key, inf->fileCount ) );
510    else if( tr_streq( key, keylen, "hashString" ) )
511        tr_bencDictAddStr( d, key, tor->info.hashString );
512    else if( tr_streq( key, keylen, "haveUnchecked" ) )
513        tr_bencDictAddInt( d, key, st->haveUnchecked );
514    else if( tr_streq( key, keylen, "haveValid" ) )
515        tr_bencDictAddInt( d, key, st->haveValid );
516    else if( tr_streq( key, keylen, "honorsSessionLimits" ) )
517        tr_bencDictAddBool( d, key, tr_torrentUsesSessionLimits( tor ) );
518    else if( tr_streq( key, keylen, "id" ) )
519        tr_bencDictAddInt( d, key, st->id );
520    else if( tr_streq( key, keylen, "isFinished" ) )
521        tr_bencDictAddBool( d, key, st->finished );
522    else if( tr_streq( key, keylen, "isPrivate" ) )
523        tr_bencDictAddBool( d, key, tr_torrentIsPrivate( tor ) );
524    else if( tr_streq( key, keylen, "leftUntilDone" ) )
525        tr_bencDictAddInt( d, key, st->leftUntilDone );
526    else if( tr_streq( key, keylen, "manualAnnounceTime" ) )
527        tr_bencDictAddInt( d, key, st->manualAnnounceTime );
528    else if( tr_streq( key, keylen, "maxConnectedPeers" ) )
529        tr_bencDictAddInt( d, key,  tr_torrentGetPeerLimit( tor ) );
530    else if( tr_streq( key, keylen, "magnetLink" ) ) {
531        char * str = tr_torrentGetMagnetLink( tor );
532        tr_bencDictAddStr( d, key, str );
533        tr_free( str );
534    }
535    else if( tr_streq( key, keylen, "metadataPercentComplete" ) )
536        tr_bencDictAddReal( d, key, st->metadataPercentComplete );
537    else if( tr_streq( key, keylen, "name" ) )
538        tr_bencDictAddStr( d, key, tr_torrentName( tor ) );
539    else if( tr_streq( key, keylen, "percentDone" ) )
540        tr_bencDictAddReal( d, key, st->percentDone );
541    else if( tr_streq( key, keylen, "peer-limit" ) )
542        tr_bencDictAddInt( d, key, tr_torrentGetPeerLimit( tor ) );
543    else if( tr_streq( key, keylen, "peers" ) )
544        addPeers( tor, tr_bencDictAdd( d, key ) );
545    else if( tr_streq( key, keylen, "peersConnected" ) )
546        tr_bencDictAddInt( d, key, st->peersConnected );
547    else if( tr_streq( key, keylen, "peersFrom" ) )
548    {
549        tr_benc *   tmp = tr_bencDictAddDict( d, key, 6 );
550        const int * f = st->peersFrom;
551        tr_bencDictAddInt( tmp, "fromCache",    f[TR_PEER_FROM_RESUME] );
552        tr_bencDictAddInt( tmp, "fromDht",      f[TR_PEER_FROM_DHT] );
553        tr_bencDictAddInt( tmp, "fromIncoming", f[TR_PEER_FROM_INCOMING] );
554        tr_bencDictAddInt( tmp, "fromLtep",     f[TR_PEER_FROM_LTEP] );
555        tr_bencDictAddInt( tmp, "fromPex",      f[TR_PEER_FROM_PEX] );
556        tr_bencDictAddInt( tmp, "fromTracker",  f[TR_PEER_FROM_TRACKER] );
557    }
558    else if( tr_streq( key, keylen, "peersGettingFromUs" ) )
559        tr_bencDictAddInt( d, key, st->peersGettingFromUs );
560    else if( tr_streq( key, keylen, "peersSendingToUs" ) )
561        tr_bencDictAddInt( d, key, st->peersSendingToUs );
562    else if( tr_streq( key, keylen, "pieces" ) ) {
563        size_t byte_count = 0;
564        void * bytes = tr_cpCreatePieceBitfield( &tor->completion, &byte_count );
565        char * str = tr_base64_encode( bytes, byte_count, NULL );
566        tr_bencDictAddStr( d, key, str!=NULL ? str : "" );
567        tr_free( str );
568        tr_free( bytes );
569    }
570    else if( tr_streq( key, keylen, "pieceCount" ) )
571        tr_bencDictAddInt( d, key, inf->pieceCount );
572    else if( tr_streq( key, keylen, "pieceSize" ) )
573        tr_bencDictAddInt( d, key, inf->pieceSize );
574    else if( tr_streq( key, keylen, "priorities" ) )
575    {
576        tr_file_index_t i;
577        tr_benc *       p = tr_bencDictAddList( d, key, inf->fileCount );
578        for( i = 0; i < inf->fileCount; ++i )
579            tr_bencListAddInt( p, inf->files[i].priority );
580    }
581    else if( tr_streq( key, keylen, "rateDownload" ) )
582        tr_bencDictAddInt( d, key, toSpeedBytes( st->pieceDownloadSpeed_KBps ) );
583    else if( tr_streq( key, keylen, "rateUpload" ) )
584        tr_bencDictAddInt( d, key, toSpeedBytes( st->pieceUploadSpeed_KBps ) );
585    else if( tr_streq( key, keylen, "recheckProgress" ) )
586        tr_bencDictAddReal( d, key, st->recheckProgress );
587    else if( tr_streq( key, keylen, "seedIdleLimit" ) )
588        tr_bencDictAddInt( d, key, tr_torrentGetIdleLimit( tor ) );
589    else if( tr_streq( key, keylen, "seedIdleMode" ) )
590        tr_bencDictAddInt( d, key, tr_torrentGetIdleMode( tor ) );
591    else if( tr_streq( key, keylen, "seedRatioLimit" ) )
592        tr_bencDictAddReal( d, key, tr_torrentGetRatioLimit( tor ) );
593    else if( tr_streq( key, keylen, "seedRatioMode" ) )
594        tr_bencDictAddInt( d, key, tr_torrentGetRatioMode( tor ) );
595    else if( tr_streq( key, keylen, "sizeWhenDone" ) )
596        tr_bencDictAddInt( d, key, st->sizeWhenDone );
597    else if( tr_streq( key, keylen, "startDate" ) )
598        tr_bencDictAddInt( d, key, st->startDate );
599    else if( tr_streq( key, keylen, "status" ) )
600        tr_bencDictAddInt( d, key, st->activity );
601    else if( tr_streq( key, keylen, "secondsDownloading" ) )
602        tr_bencDictAddInt( d, key, st->secondsDownloading );
603    else if( tr_streq( key, keylen, "secondsSeeding" ) )
604        tr_bencDictAddInt( d, key, st->secondsSeeding );
605    else if( tr_streq( key, keylen, "trackers" ) )
606        addTrackers( inf, tr_bencDictAddList( d, key, inf->trackerCount ) );
607    else if( tr_streq( key, keylen, "trackerStats" ) ) {
608        int n;
609        tr_tracker_stat * s = tr_torrentTrackers( tor, &n );
610        addTrackerStats( s, n, tr_bencDictAddList( d, key, n ) );
611        tr_torrentTrackersFree( s, n );
612    }
613    else if( tr_streq( key, keylen, "torrentFile" ) )
614        tr_bencDictAddStr( d, key, inf->torrent );
615    else if( tr_streq( key, keylen, "totalSize" ) )
616        tr_bencDictAddInt( d, key, inf->totalSize );
617    else if( tr_streq( key, keylen, "uploadedEver" ) )
618        tr_bencDictAddInt( d, key, st->uploadedEver );
619    else if( tr_streq( key, keylen, "uploadLimit" ) )
620        tr_bencDictAddInt( d, key, tr_torrentGetSpeedLimit_KBps( tor, TR_UP ) );
621    else if( tr_streq( key, keylen, "uploadLimited" ) )
622        tr_bencDictAddBool( d, key, tr_torrentUsesSpeedLimit( tor, TR_UP ) );
623    else if( tr_streq( key, keylen, "uploadRatio" ) )
624        tr_bencDictAddReal( d, key, st->ratio );
625    else if( tr_streq( key, keylen, "wanted" ) )
626    {
627        tr_file_index_t i;
628        tr_benc *       w = tr_bencDictAddList( d, key, inf->fileCount );
629        for( i = 0; i < inf->fileCount; ++i )
630            tr_bencListAddInt( w, inf->files[i].dnd ? 0 : 1 );
631    }
632    else if( tr_streq( key, keylen, "webseeds" ) )
633        addWebseeds( inf, tr_bencDictAddList( d, key, inf->webseedCount ) );
634    else if( tr_streq( key, keylen, "webseedsSendingToUs" ) )
635        tr_bencDictAddInt( d, key, st->webseedsSendingToUs );
636}
637
638static void
639addInfo( const tr_torrent * tor, tr_benc * d, tr_benc * fields )
640{
641    const char * str;
642    const int n = tr_bencListSize( fields );
643
644    tr_bencInitDict( d, n );
645
646    if( n > 0 )
647    {
648        int i;
649        const tr_info const * inf = tr_torrentInfo( tor );
650        const tr_stat const * st = tr_torrentStat( (tr_torrent*)tor );
651
652        for( i=0; i<n; ++i )
653            if( tr_bencGetStr( tr_bencListChild( fields, i ), &str ) )
654                addField( tor, inf, st, d, str );
655    }
656}
657
658static const char*
659torrentGet( tr_session               * session,
660            tr_benc                  * args_in,
661            tr_benc                  * args_out,
662            struct tr_rpc_idle_data  * idle_data UNUSED )
663{
664    int           i, torrentCount;
665    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
666    tr_benc *     list = tr_bencDictAddList( args_out, "torrents", torrentCount );
667    tr_benc *     fields;
668    const char *  msg = NULL;
669    const char *  strVal;
670
671    assert( idle_data == NULL );
672
673    if( tr_bencDictFindStr( args_in, "ids", &strVal ) && !strcmp( strVal, "recently-active" ) ) {
674        int n = 0;
675        tr_benc * d;
676        const time_t now = tr_time( );
677        const int interval = RECENTLY_ACTIVE_SECONDS;
678        tr_benc * removed_out = tr_bencDictAddList( args_out, "removed", 0 );
679        while(( d = tr_bencListChild( &session->removedTorrents, n++ ))) {
680            int64_t intVal;
681            if( tr_bencDictFindInt( d, "date", &intVal ) && ( intVal >= now - interval ) ) {
682                tr_bencDictFindInt( d, "id", &intVal );
683                tr_bencListAddInt( removed_out, intVal );
684            }
685        }
686    }
687
688    if( !tr_bencDictFindList( args_in, "fields", &fields ) )
689        msg = "no fields specified";
690    else for( i = 0; i < torrentCount; ++i )
691        addInfo( torrents[i], tr_bencListAdd( list ), fields );
692
693    tr_free( torrents );
694    return msg;
695}
696
697/***
698****
699***/
700
701static const char*
702setFilePriorities( tr_torrent * tor,
703                   int          priority,
704                   tr_benc *    list )
705{
706    int i;
707    int64_t tmp;
708    int fileCount = 0;
709    const int n = tr_bencListSize( list );
710    const char * errmsg = NULL;
711    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
712
713    if( n )
714    {
715        for( i = 0; i < n; ++i ) {
716            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) {
717                if( 0 <= tmp && tmp < tor->info.fileCount ) {
718                    files[fileCount++] = tmp;
719                } else {
720                    errmsg = "file index out of range";
721                }
722            }
723        }
724    }
725    else /* if empty set, apply to all */
726    {
727        tr_file_index_t t;
728        for( t = 0; t < tor->info.fileCount; ++t )
729            files[fileCount++] = t;
730    }
731
732    if( fileCount )
733        tr_torrentSetFilePriorities( tor, files, fileCount, priority );
734
735    tr_free( files );
736    return errmsg;
737}
738
739static const char*
740setFileDLs( tr_torrent * tor,
741            int          do_download,
742            tr_benc *    list )
743{
744    int i;
745    int64_t tmp;
746    int fileCount = 0;
747    const int n = tr_bencListSize( list );
748    const char * errmsg = NULL;
749    tr_file_index_t * files = tr_new0( tr_file_index_t, tor->info.fileCount );
750
751    if( n ) /* if argument list, process them */
752    {
753        for( i = 0; i < n; ++i ) {
754            if( tr_bencGetInt( tr_bencListChild( list, i ), &tmp ) ) {
755                if( 0 <= tmp && tmp < tor->info.fileCount ) {
756                    files[fileCount++] = tmp;
757                } else {
758                    errmsg = "file index out of range";
759                }
760            }
761        }
762    }
763    else /* if empty set, apply to all */
764    {
765        tr_file_index_t t;
766        for( t = 0; t < tor->info.fileCount; ++t )
767            files[fileCount++] = t;
768    }
769
770    if( fileCount )
771        tr_torrentSetFileDLs( tor, files, fileCount, do_download );
772
773    tr_free( files );
774    return errmsg;
775}
776
777static bool
778findAnnounceUrl( const tr_tracker_info * t, int n, const char * url, int * pos )
779{
780    int i;
781    bool found = false;
782
783    for( i=0; i<n; ++i )
784    {
785        if( !strcmp( t[i].announce, url ) )
786        {
787            found = true;
788            if( pos ) *pos = i;
789            break;
790        }
791    }
792
793    return found;
794}
795
796static int
797copyTrackers( tr_tracker_info * tgt, const tr_tracker_info * src, int n )
798{
799    int i;
800    int maxTier = -1;
801
802    for( i=0; i<n; ++i )
803    {
804        tgt[i].tier = src[i].tier;
805        tgt[i].announce = tr_strdup( src[i].announce );
806        maxTier = MAX( maxTier, src[i].tier );
807    }
808
809    return maxTier;
810}
811
812static void
813freeTrackers( tr_tracker_info * trackers, int n )
814{
815    int i;
816
817    for( i=0; i<n; ++i )
818        tr_free( trackers[i].announce );
819
820    tr_free( trackers );
821}
822
823static const char*
824addTrackerUrls( tr_torrent * tor, tr_benc * urls )
825{
826    int i;
827    int n;
828    int tier;
829    tr_benc * val;
830    tr_tracker_info * trackers;
831    bool changed = false;
832    const tr_info * inf = tr_torrentInfo( tor );
833    const char * errmsg = NULL;
834
835    /* make a working copy of the existing announce list */
836    n = inf->trackerCount;
837    trackers = tr_new0( tr_tracker_info, n + tr_bencListSize( urls ) );
838    tier = copyTrackers( trackers, inf->trackers, n );
839
840    /* and add the new ones */
841    i = 0;
842    while(( val = tr_bencListChild( urls, i++ ) ))
843    {
844        const char * announce = NULL;
845
846        if(    tr_bencGetStr( val, &announce )
847            && tr_urlIsValidTracker( announce )
848            && !findAnnounceUrl( trackers, n, announce, NULL ) )
849        {
850            trackers[n].tier = ++tier; /* add a new tier */
851            trackers[n].announce = tr_strdup( announce );
852            ++n;
853            changed = true;
854        }
855    }
856
857    if( !changed )
858        errmsg = "invalid argument";
859    else if( !tr_torrentSetAnnounceList( tor, trackers, n ) )
860        errmsg = "error setting announce list";
861
862    freeTrackers( trackers, n );
863    return errmsg;
864}
865
866static const char*
867replaceTrackers( tr_torrent * tor, tr_benc * urls )
868{
869    int i;
870    tr_benc * pair[2];
871    tr_tracker_info * trackers;
872    bool changed = false;
873    const tr_info * inf = tr_torrentInfo( tor );
874    const int n = inf->trackerCount;
875    const char * errmsg = NULL;
876
877    /* make a working copy of the existing announce list */
878    trackers = tr_new0( tr_tracker_info, n );
879    copyTrackers( trackers, inf->trackers, n );
880
881    /* make the substitutions... */
882    i = 0;
883    while(((pair[0] = tr_bencListChild(urls,i))) &&
884          ((pair[1] = tr_bencListChild(urls,i+1))))
885    {
886        int64_t pos;
887        const char * newval;
888
889        if(    tr_bencGetInt( pair[0], &pos )
890            && tr_bencGetStr( pair[1], &newval )
891            && tr_urlIsValid( newval, -1 )
892            && pos < n
893            && pos >= 0 )
894        {
895            tr_free( trackers[pos].announce );
896            trackers[pos].announce = tr_strdup( newval );
897            changed = true;
898        }
899
900        i += 2;
901    }
902
903    if( !changed )
904        errmsg = "invalid argument";
905    else if( !tr_torrentSetAnnounceList( tor, trackers, n ) )
906        errmsg = "error setting announce list";
907
908    freeTrackers( trackers, n );
909    return errmsg;
910}
911
912static const char*
913removeTrackers( tr_torrent * tor, tr_benc * ids )
914{
915    int i;
916    int n;
917    int t = 0;
918    int dup = -1;
919    int * tids;
920    tr_benc * val;
921    tr_tracker_info * trackers;
922    bool changed = false;
923    const tr_info * inf = tr_torrentInfo( tor );
924    const char * errmsg = NULL;
925
926    /* make a working copy of the existing announce list */
927    n = inf->trackerCount;
928    tids = tr_new0( int, n );
929    trackers = tr_new0( tr_tracker_info, n );
930    copyTrackers( trackers, inf->trackers, n );
931
932    /* remove the ones specified in the urls list */
933    i = 0;
934    while(( val = tr_bencListChild( ids, i++ )))
935    {
936        int64_t pos;
937
938        if(    tr_bencGetInt( val, &pos )
939            && pos < n
940            && pos >= 0 )
941            tids[t++] = pos;
942    }
943
944    /* sort trackerIds and remove from largest to smallest so there is no need to recacluate array indicies */
945    qsort( tids, t, sizeof(int), compareInt );
946    while( t-- )
947    {
948        /* check for duplicates */
949        if( tids[t] == dup )
950            continue;
951        tr_removeElementFromArray( trackers, tids[t], sizeof( tr_tracker_info ), n-- );
952        dup = tids[t];
953        changed = true;
954    }
955
956    if( !changed )
957        errmsg = "invalid argument";
958    else if( !tr_torrentSetAnnounceList( tor, trackers, n ) )
959        errmsg = "error setting announce list";
960
961    freeTrackers( trackers, n );
962    tr_free( tids );
963    return errmsg;
964}
965
966static const char*
967torrentSet( tr_session               * session,
968            tr_benc                  * args_in,
969            tr_benc                  * args_out UNUSED,
970            struct tr_rpc_idle_data  * idle_data UNUSED )
971{
972    const char * errmsg = NULL;
973    int i, torrentCount;
974    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
975
976    assert( idle_data == NULL );
977
978    for( i = 0; i < torrentCount; ++i )
979    {
980        int64_t      tmp;
981        double       d;
982        tr_benc *    files;
983        tr_benc *    trackers;
984        bool         boolVal;
985        tr_torrent * tor = torrents[i];
986
987        if( tr_bencDictFindInt( args_in, "bandwidthPriority", &tmp ) )
988            if( tr_isPriority( tmp ) )
989                tr_torrentSetPriority( tor, tmp );
990        if( !errmsg && tr_bencDictFindList( args_in, "files-unwanted", &files ) )
991            errmsg = setFileDLs( tor, false, files );
992        if( !errmsg && tr_bencDictFindList( args_in, "files-wanted", &files ) )
993            errmsg = setFileDLs( tor, true, files );
994        if( tr_bencDictFindInt( args_in, "peer-limit", &tmp ) )
995            tr_torrentSetPeerLimit( tor, tmp );
996        if( !errmsg &&  tr_bencDictFindList( args_in, "priority-high", &files ) )
997            errmsg = setFilePriorities( tor, TR_PRI_HIGH, files );
998        if( !errmsg && tr_bencDictFindList( args_in, "priority-low", &files ) )
999            errmsg = setFilePriorities( tor, TR_PRI_LOW, files );
1000        if( !errmsg && tr_bencDictFindList( args_in, "priority-normal", &files ) )
1001            errmsg = setFilePriorities( tor, TR_PRI_NORMAL, files );
1002        if( tr_bencDictFindInt( args_in, "downloadLimit", &tmp ) )
1003            tr_torrentSetSpeedLimit_KBps( tor, TR_DOWN, tmp );
1004        if( tr_bencDictFindBool( args_in, "downloadLimited", &boolVal ) )
1005            tr_torrentUseSpeedLimit( tor, TR_DOWN, boolVal );
1006        if( tr_bencDictFindBool( args_in, "honorsSessionLimits", &boolVal ) )
1007            tr_torrentUseSessionLimits( tor, boolVal );
1008        if( tr_bencDictFindInt( args_in, "uploadLimit", &tmp ) )
1009            tr_torrentSetSpeedLimit_KBps( tor, TR_UP, tmp );
1010        if( tr_bencDictFindBool( args_in, "uploadLimited", &boolVal ) )
1011            tr_torrentUseSpeedLimit( tor, TR_UP, boolVal );
1012        if( tr_bencDictFindInt( args_in, "seedIdleLimit", &tmp ) )
1013            tr_torrentSetIdleLimit( tor, tmp );
1014        if( tr_bencDictFindInt( args_in, "seedIdleMode", &tmp ) )
1015            tr_torrentSetIdleMode( tor, tmp );
1016        if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
1017            tr_torrentSetRatioLimit( tor, d );
1018        if( tr_bencDictFindInt( args_in, "seedRatioMode", &tmp ) )
1019            tr_torrentSetRatioMode( tor, tmp );
1020        if( !errmsg && tr_bencDictFindList( args_in, "trackerAdd", &trackers ) )
1021            errmsg = addTrackerUrls( tor, trackers );
1022        if( !errmsg && tr_bencDictFindList( args_in, "trackerRemove", &trackers ) )
1023            errmsg = removeTrackers( tor, trackers );
1024        if( !errmsg && tr_bencDictFindList( args_in, "trackerReplace", &trackers ) )
1025            errmsg = replaceTrackers( tor, trackers );
1026        notify( session, TR_RPC_TORRENT_CHANGED, tor );
1027    }
1028
1029    tr_free( torrents );
1030    return errmsg;
1031}
1032
1033static const char*
1034torrentSetLocation( tr_session               * session,
1035                    tr_benc                  * args_in,
1036                    tr_benc                  * args_out UNUSED,
1037                    struct tr_rpc_idle_data  * idle_data UNUSED )
1038{
1039    const char * errmsg = NULL;
1040    const char * location = NULL;
1041
1042    assert( idle_data == NULL );
1043
1044    if( !tr_bencDictFindStr( args_in, "location", &location ) )
1045    {
1046        errmsg = "no location";
1047    }
1048    else
1049    {
1050        bool move = false;
1051        int i, torrentCount;
1052        tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
1053
1054        tr_bencDictFindBool( args_in, "move", &move );
1055
1056        for( i=0; i<torrentCount; ++i )
1057        {
1058            tr_torrent * tor = torrents[i];
1059            tr_torrentSetLocation( tor, location, move, NULL, NULL );
1060            notify( session, TR_RPC_TORRENT_MOVED, tor );
1061        }
1062
1063        tr_free( torrents );
1064    }
1065
1066    return errmsg;
1067}
1068
1069/***
1070****
1071***/
1072
1073static void
1074portTested( tr_session       * session UNUSED,
1075            bool               did_connect UNUSED,
1076            bool               did_timeout UNUSED,
1077            long               response_code,
1078            const void       * response,
1079            size_t             response_byte_count,
1080            void             * user_data )
1081{
1082    char result[1024];
1083    struct tr_rpc_idle_data * data = user_data;
1084
1085    if( response_code != 200 )
1086    {
1087        tr_snprintf( result, sizeof( result ), "portTested: http error %ld: %s",
1088                     response_code, tr_webGetResponseStr( response_code ) );
1089    }
1090    else /* success */
1091    {
1092        const bool isOpen = response_byte_count && *(char*)response == '1';
1093        tr_bencDictAddBool( data->args_out, "port-is-open", isOpen );
1094        tr_snprintf( result, sizeof( result ), "success" );
1095    }
1096
1097    tr_idle_function_done( data, result );
1098}
1099
1100static const char*
1101portTest( tr_session               * session,
1102          tr_benc                  * args_in UNUSED,
1103          tr_benc                  * args_out UNUSED,
1104          struct tr_rpc_idle_data  * idle_data )
1105{
1106    const int port = tr_sessionGetPeerPort( session );
1107    char * url = tr_strdup_printf( "http://portcheck.transmissionbt.com/%d", port );
1108    tr_webRun( session, url, NULL, NULL, portTested, idle_data );
1109    tr_free( url );
1110    return NULL;
1111}
1112
1113/***
1114****
1115***/
1116
1117static void
1118gotNewBlocklist( tr_session       * session,
1119                 bool               did_connect UNUSED,
1120                 bool               did_timeout UNUSED,
1121                 long               response_code,
1122                 const void       * response,
1123                 size_t             response_byte_count,
1124                 void             * user_data )
1125{
1126    char result[1024];
1127    struct tr_rpc_idle_data * data = user_data;
1128
1129    *result = '\0';
1130
1131    if( response_code != 200 )
1132    {
1133        tr_snprintf( result, sizeof( result ), "gotNewBlocklist: http error %ld: %s",
1134                     response_code, tr_webGetResponseStr( response_code ) );
1135    }
1136    else /* successfully fetched the blocklist... */
1137    {
1138        int fd;
1139        const char * configDir = tr_sessionGetConfigDir( session );
1140        char * filename = tr_buildPath( configDir, "blocklist.tmp", NULL );
1141
1142        errno = 0;
1143
1144        fd = tr_open_file_for_writing( filename );
1145        if( fd < 0 )
1146            tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename, tr_strerror( errno ) );
1147
1148        if( !errno ) {
1149            const char * buf = response;
1150            size_t buflen = response_byte_count;
1151            while( buflen > 0 ) {
1152                int n = write( fd, buf, buflen );
1153                if( n < 0 ) {
1154                    tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename, tr_strerror( errno ) );
1155                    break;
1156                }
1157                buf += n;
1158                buflen -= n;
1159            }
1160            tr_close_file( fd );
1161        }
1162
1163#ifdef HAVE_ZLIB
1164        if( !errno )
1165        {
1166            char * filename2 = tr_buildPath( configDir, "blocklist.txt.tmp", NULL );
1167            fd = tr_open_file_for_writing( filename2 );
1168            if( fd < 0 )
1169                tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename2, tr_strerror( errno ) );
1170            else {
1171                gzFile gzf = gzopen( filename, "r" );
1172                if( gzf ) {
1173                    const size_t buflen = 1024 * 128; /* 128 KiB buffer */
1174                    uint8_t * buf = tr_valloc( buflen );
1175                    for( ;; ) {
1176                        int n = gzread( gzf, buf, buflen );
1177                        if( n < 0 ) /* error */
1178                            tr_snprintf( result, sizeof( result ), _( "Error reading \"%1$s\": %2$s" ), filename, gzerror( gzf, NULL ) );
1179                        if( n < 1 ) /* error or EOF */
1180                            break;
1181                        if( write( fd, buf, n ) < 0 )
1182                            tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename2, tr_strerror( errno ) );
1183                    }
1184                    tr_free( buf );
1185                    gzclose( gzf );
1186                } else {
1187                    tr_snprintf( result, sizeof( result ), _( "Error opening \"%1$s\": %2$s" ), filename, tr_strerror( errno ) );
1188                }
1189                tr_close_file( fd );
1190            }
1191
1192            if( *result )
1193                tr_err( "%s", result );
1194
1195            unlink( filename );
1196            tr_free( filename );
1197            filename = filename2;
1198        }
1199#endif
1200
1201        if( !errno ) {
1202            /* feed it to the session and give the client a response */
1203            const int rule_count = tr_blocklistSetContent( session, filename );
1204            tr_bencDictAddInt( data->args_out, "blocklist-size", rule_count );
1205            tr_snprintf( result, sizeof( result ), "success" );
1206        }
1207
1208        unlink( filename );
1209        tr_free( filename );
1210    }
1211
1212    tr_idle_function_done( data, result );
1213}
1214
1215static const char*
1216blocklistUpdate( tr_session               * session,
1217                 tr_benc                  * args_in UNUSED,
1218                 tr_benc                  * args_out UNUSED,
1219                 struct tr_rpc_idle_data  * idle_data )
1220{
1221    tr_webRun( session, session->blocklist_url, NULL, NULL, gotNewBlocklist, idle_data );
1222    return NULL;
1223}
1224
1225/***
1226****
1227***/
1228
1229static void
1230addTorrentImpl( struct tr_rpc_idle_data * data, tr_ctor * ctor )
1231{
1232    int err = 0;
1233    const char * result = NULL;
1234    tr_torrent * tor = tr_torrentNew( ctor, &err );
1235
1236    tr_ctorFree( ctor );
1237
1238    if( tor )
1239    {
1240        tr_benc fields;
1241        tr_bencInitList( &fields, 3 );
1242        tr_bencListAddStr( &fields, "id" );
1243        tr_bencListAddStr( &fields, "name" );
1244        tr_bencListAddStr( &fields, "hashString" );
1245        addInfo( tor, tr_bencDictAdd( data->args_out, "torrent-added" ), &fields );
1246        notify( data->session, TR_RPC_TORRENT_ADDED, tor );
1247        tr_bencFree( &fields );
1248    }
1249    else if( err == TR_PARSE_DUPLICATE )
1250    {
1251        result = "duplicate torrent";
1252    }
1253    else if( err == TR_PARSE_ERR )
1254    {
1255        result = "invalid or corrupt torrent file";
1256    }
1257
1258    tr_idle_function_done( data, result );
1259}
1260
1261
1262struct add_torrent_idle_data
1263{
1264    struct tr_rpc_idle_data * data;
1265    tr_ctor * ctor;
1266};
1267
1268static void
1269gotMetadataFromURL( tr_session       * session UNUSED,
1270                    bool               did_connect UNUSED,
1271                    bool               did_timeout UNUSED,
1272                    long               response_code,
1273                    const void       * response,
1274                    size_t             response_byte_count,
1275                    void             * user_data )
1276{
1277    struct add_torrent_idle_data * data = user_data;
1278
1279    dbgmsg( "torrentAdd: HTTP response code was %ld (%s); response length was %zu bytes",
1280            response_code, tr_webGetResponseStr( response_code ), response_byte_count );
1281
1282    if( response_code==200 || response_code==221 ) /* http or ftp success.. */
1283    {
1284        tr_ctorSetMetainfo( data->ctor, response, response_byte_count );
1285        addTorrentImpl( data->data, data->ctor );
1286    }
1287    else
1288    {
1289        char result[1024];
1290        tr_snprintf( result, sizeof( result ), "gotMetadataFromURL: http error %ld: %s",
1291                     response_code, tr_webGetResponseStr( response_code ) );
1292        tr_idle_function_done( data->data, result );
1293    }
1294
1295    tr_free( data );
1296}
1297
1298static bool
1299isCurlURL( const char * filename )
1300{
1301    if( filename == NULL )
1302        return false;
1303
1304    return !strncmp( filename, "ftp://", 6 ) ||
1305           !strncmp( filename, "http://", 7 ) ||
1306           !strncmp( filename, "https://", 8 );
1307}
1308
1309static tr_file_index_t*
1310fileListFromList( tr_benc * list, tr_file_index_t * setmeCount )
1311{
1312    size_t i;
1313    const size_t childCount = tr_bencListSize( list );
1314    tr_file_index_t n = 0;
1315    tr_file_index_t * files = tr_new0( tr_file_index_t, childCount );
1316
1317    for( i=0; i<childCount; ++i ) {
1318        int64_t intVal;
1319        if( tr_bencGetInt( tr_bencListChild( list, i ), &intVal ) )
1320            files[n++] = (tr_file_index_t)intVal;
1321    }
1322
1323    *setmeCount = n;
1324    return files;
1325}
1326
1327static const char*
1328torrentAdd( tr_session               * session,
1329            tr_benc                  * args_in,
1330            tr_benc                  * args_out UNUSED,
1331            struct tr_rpc_idle_data  * idle_data )
1332{
1333    const char * filename = NULL;
1334    const char * metainfo_base64 = NULL;
1335
1336    assert( idle_data != NULL );
1337
1338    tr_bencDictFindStr( args_in, "filename", &filename );
1339    tr_bencDictFindStr( args_in, "metainfo", &metainfo_base64 );
1340    if( !filename && !metainfo_base64 )
1341        return "no filename or metainfo specified";
1342    else
1343    {
1344        int64_t      i;
1345        bool         boolVal;
1346        tr_benc    * l;
1347        const char * str;
1348        const char * cookies = NULL;
1349        tr_ctor    * ctor = tr_ctorNew( session );
1350
1351        /* set the optional arguments */
1352
1353        tr_bencDictFindStr( args_in, "cookies", &cookies );
1354
1355        if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1356            tr_ctorSetDownloadDir( ctor, TR_FORCE, str );
1357
1358        if( tr_bencDictFindBool( args_in, "paused", &boolVal ) )
1359            tr_ctorSetPaused( ctor, TR_FORCE, boolVal );
1360
1361        if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
1362            tr_ctorSetPeerLimit( ctor, TR_FORCE, i );
1363
1364        if( tr_bencDictFindInt( args_in, "bandwidthPriority", &i ) )
1365            tr_ctorSetBandwidthPriority( ctor, i );
1366
1367        if( tr_bencDictFindList( args_in, "files-unwanted", &l ) ) {
1368            tr_file_index_t fileCount;
1369            tr_file_index_t * files = fileListFromList( l, &fileCount );
1370            tr_ctorSetFilesWanted( ctor, files, fileCount, false );
1371            tr_free( files );
1372        }
1373        if( tr_bencDictFindList( args_in, "files-wanted", &l ) ) {
1374            tr_file_index_t fileCount;
1375            tr_file_index_t * files = fileListFromList( l, &fileCount );
1376            tr_ctorSetFilesWanted( ctor, files, fileCount, true );
1377            tr_free( files );
1378        }
1379
1380        if( tr_bencDictFindList( args_in, "priority-low", &l ) ) {
1381            tr_file_index_t fileCount;
1382            tr_file_index_t * files = fileListFromList( l, &fileCount );
1383            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_LOW );
1384            tr_free( files );
1385        }
1386        if( tr_bencDictFindList( args_in, "priority-normal", &l ) ) {
1387            tr_file_index_t fileCount;
1388            tr_file_index_t * files = fileListFromList( l, &fileCount );
1389            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_NORMAL );
1390            tr_free( files );
1391        }
1392        if( tr_bencDictFindList( args_in, "priority-high", &l ) ) {
1393            tr_file_index_t fileCount;
1394            tr_file_index_t * files = fileListFromList( l, &fileCount );
1395            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_HIGH );
1396            tr_free( files );
1397        }
1398
1399        dbgmsg( "torrentAdd: filename is \"%s\"", filename ? filename : "(null)" );
1400
1401        if( isCurlURL( filename ) )
1402        {
1403            struct add_torrent_idle_data * d = tr_new0( struct add_torrent_idle_data, 1 );
1404            d->data = idle_data;
1405            d->ctor = ctor;
1406            tr_webRun( session, filename, NULL, cookies, gotMetadataFromURL, d );
1407        }
1408        else
1409        {
1410            char * fname = tr_strstrip( tr_strdup( filename ) );
1411
1412            if( fname == NULL )
1413            {
1414                int len;
1415                char * metainfo = tr_base64_decode( metainfo_base64, -1, &len );
1416                tr_ctorSetMetainfo( ctor, (uint8_t*)metainfo, len );
1417                tr_free( metainfo );
1418            }
1419            else if( !strncmp( fname, "magnet:?", 8 ) )
1420            {
1421                tr_ctorSetMetainfoFromMagnetLink( ctor, fname );
1422            }
1423            else
1424            {
1425                tr_ctorSetMetainfoFromFile( ctor, fname );
1426            }
1427
1428            addTorrentImpl( idle_data, ctor );
1429
1430            tr_free( fname );
1431        }
1432
1433    }
1434
1435    return NULL;
1436}
1437
1438/***
1439****
1440***/
1441
1442static const char*
1443sessionSet( tr_session               * session,
1444            tr_benc                  * args_in,
1445            tr_benc                  * args_out UNUSED,
1446            struct tr_rpc_idle_data  * idle_data UNUSED )
1447{
1448    int64_t      i;
1449    double       d;
1450    bool         boolVal;
1451    const char * str;
1452
1453    assert( idle_data == NULL );
1454
1455    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, &i ) )
1456        tr_sessionSetCacheLimit_MB( session, i );
1457    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_UP_KBps, &i ) )
1458        tr_sessionSetAltSpeed_KBps( session, TR_UP, i );
1459    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, &i ) )
1460        tr_sessionSetAltSpeed_KBps( session, TR_DOWN, i );
1461    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_ENABLED, &boolVal ) )
1462        tr_sessionUseAltSpeed( session, boolVal );
1463    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &i ) )
1464        tr_sessionSetAltSpeedBegin( session, i );
1465    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_END, &i ) )
1466        tr_sessionSetAltSpeedEnd( session, i );
1467    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &i ) )
1468        tr_sessionSetAltSpeedDay( session, i );
1469    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &boolVal ) )
1470        tr_sessionUseAltSpeedTime( session, boolVal );
1471    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_BLOCKLIST_ENABLED, &boolVal ) )
1472        tr_blocklistSetEnabled( session, boolVal );
1473    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_BLOCKLIST_URL, &str ) )
1474        tr_blocklistSetURL( session, str );
1475    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1476        tr_sessionSetDownloadDir( session, str );
1477    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_INCOMPLETE_DIR, &str ) )
1478        tr_sessionSetIncompleteDir( session, str );
1479    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, &boolVal ) )
1480        tr_sessionSetIncompleteDirEnabled( session, boolVal );
1481    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &i ) )
1482        tr_sessionSetPeerLimit( session, i );
1483    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i ) )
1484        tr_sessionSetPeerLimitPerTorrent( session, i );
1485    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) )
1486        tr_sessionSetPexEnabled( session, boolVal );
1487    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DHT_ENABLED, &boolVal ) )
1488        tr_sessionSetDHTEnabled( session, boolVal );
1489    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_UTP_ENABLED, &boolVal ) )
1490        tr_sessionSetUTPEnabled( session, boolVal );
1491    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_LPD_ENABLED, &boolVal ) )
1492        tr_sessionSetLPDEnabled( session, boolVal );
1493    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, &boolVal ) )
1494        tr_sessionSetPeerPortRandomOnStart( session, boolVal );
1495    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_PORT, &i ) )
1496        tr_sessionSetPeerPort( session, i );
1497    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) )
1498        tr_sessionSetPortForwardingEnabled( session, boolVal );
1499    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_RENAME_PARTIAL_FILES, &boolVal ) )
1500        tr_sessionSetIncompleteFileNamingEnabled( session, boolVal );
1501    if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
1502        tr_sessionSetRatioLimit( session, d );
1503    if( tr_bencDictFindBool( args_in, "seedRatioLimited", &boolVal ) )
1504        tr_sessionSetRatioLimited( session, boolVal );
1505    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_IDLE_LIMIT, &i ) )
1506        tr_sessionSetIdleLimit( session, i );
1507    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, &boolVal ) )
1508        tr_sessionSetIdleLimited( session, boolVal );
1509    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_START, &boolVal ) )
1510        tr_sessionSetPaused( session, !boolVal );
1511    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, &str ) )
1512        tr_sessionSetTorrentDoneScript( session, str );
1513    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, &boolVal ) )
1514        tr_sessionSetTorrentDoneScriptEnabled( session, boolVal );
1515    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_TRASH_ORIGINAL, &boolVal ) )
1516        tr_sessionSetDeleteSource( session, boolVal );
1517    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_DSPEED_KBps, &i ) )
1518        tr_sessionSetSpeedLimit_KBps( session, TR_DOWN, i );
1519    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DSPEED_ENABLED, &boolVal ) )
1520        tr_sessionLimitSpeed( session, TR_DOWN, boolVal );
1521    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_USPEED_KBps, &i ) )
1522        tr_sessionSetSpeedLimit_KBps( session, TR_UP, i );
1523    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_USPEED_ENABLED, &boolVal ) )
1524        tr_sessionLimitSpeed( session, TR_UP, boolVal );
1525    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_ENCRYPTION, &str ) ) {
1526        if( !strcmp( str, "required" ) )
1527            tr_sessionSetEncryption( session, TR_ENCRYPTION_REQUIRED );
1528        else if( !strcmp( str, "tolerated" ) )
1529            tr_sessionSetEncryption( session, TR_CLEAR_PREFERRED );
1530        else
1531            tr_sessionSetEncryption( session, TR_ENCRYPTION_PREFERRED );
1532    }
1533
1534    notify( session, TR_RPC_SESSION_CHANGED, NULL );
1535
1536    return NULL;
1537}
1538
1539static const char*
1540sessionStats( tr_session               * session,
1541              tr_benc                  * args_in UNUSED,
1542              tr_benc                  * args_out,
1543              struct tr_rpc_idle_data  * idle_data UNUSED )
1544{
1545    int running = 0;
1546    int total = 0;
1547    tr_benc * d;
1548    tr_session_stats currentStats = { 0.0f, 0, 0, 0, 0, 0 };
1549    tr_session_stats cumulativeStats = { 0.0f, 0, 0, 0, 0, 0 };
1550    tr_torrent * tor = NULL;
1551
1552    assert( idle_data == NULL );
1553
1554    while(( tor = tr_torrentNext( session, tor ))) {
1555        ++total;
1556        if( tor->isRunning )
1557            ++running;
1558    }
1559
1560    tr_sessionGetStats( session, &currentStats );
1561    tr_sessionGetCumulativeStats( session, &cumulativeStats );
1562
1563    tr_bencDictAddInt ( args_out, "activeTorrentCount", running );
1564    tr_bencDictAddReal( args_out, "downloadSpeed", tr_sessionGetPieceSpeed_Bps( session, TR_DOWN ) );
1565    tr_bencDictAddInt ( args_out, "pausedTorrentCount", total - running );
1566    tr_bencDictAddInt ( args_out, "torrentCount", total );
1567    tr_bencDictAddReal( args_out, "uploadSpeed", tr_sessionGetPieceSpeed_Bps( session, TR_UP ) );
1568
1569    d = tr_bencDictAddDict( args_out, "cumulative-stats", 5 );
1570    tr_bencDictAddInt( d, "downloadedBytes", cumulativeStats.downloadedBytes );
1571    tr_bencDictAddInt( d, "filesAdded", cumulativeStats.filesAdded );
1572    tr_bencDictAddInt( d, "secondsActive", cumulativeStats.secondsActive );
1573    tr_bencDictAddInt( d, "sessionCount", cumulativeStats.sessionCount );
1574    tr_bencDictAddInt( d, "uploadedBytes", cumulativeStats.uploadedBytes );
1575
1576    d = tr_bencDictAddDict( args_out, "current-stats", 5 );
1577    tr_bencDictAddInt( d, "downloadedBytes", currentStats.downloadedBytes );
1578    tr_bencDictAddInt( d, "filesAdded", currentStats.filesAdded );
1579    tr_bencDictAddInt( d, "secondsActive", currentStats.secondsActive );
1580    tr_bencDictAddInt( d, "sessionCount", currentStats.sessionCount );
1581    tr_bencDictAddInt( d, "uploadedBytes", currentStats.uploadedBytes );
1582
1583    return NULL;
1584}
1585
1586static const char*
1587sessionGet( tr_session               * s,
1588            tr_benc                  * args_in UNUSED,
1589            tr_benc                  * args_out,
1590            struct tr_rpc_idle_data  * idle_data UNUSED )
1591{
1592    const char * str;
1593    tr_benc * d = args_out;
1594
1595    assert( idle_data == NULL );
1596    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP_KBps, tr_sessionGetAltSpeed_KBps(s,TR_UP) );
1597    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, tr_sessionGetAltSpeed_KBps(s,TR_DOWN) );
1598    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED, tr_sessionUsesAltSpeed(s) );
1599    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, tr_sessionGetAltSpeedBegin(s) );
1600    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,tr_sessionGetAltSpeedEnd(s) );
1601    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,tr_sessionGetAltSpeedDay(s) );
1602    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, tr_sessionUsesAltSpeedTime(s) );
1603    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, tr_blocklistIsEnabled( s ) );
1604    tr_bencDictAddStr ( d, TR_PREFS_KEY_BLOCKLIST_URL, tr_blocklistGetURL( s ) );
1605    tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, tr_sessionGetCacheLimit_MB( s ) );
1606    tr_bencDictAddInt ( d, "blocklist-size", tr_blocklistGetRuleCount( s ) );
1607    tr_bencDictAddStr ( d, "config-dir", tr_sessionGetConfigDir( s ) );
1608    tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR, tr_sessionGetDownloadDir( s ) );
1609    tr_bencDictAddInt ( d, "download-dir-free-space",  tr_sessionGetDownloadDirFreeSpace( s ) );
1610    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, tr_sessionGetPeerLimit( s ) );
1611    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT, tr_sessionGetPeerLimitPerTorrent( s ) );
1612    tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR, tr_sessionGetIncompleteDir( s ) );
1613    tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, tr_sessionIsIncompleteDirEnabled( s ) );
1614    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED, tr_sessionIsPexEnabled( s ) );
1615    tr_bencDictAddBool( d, TR_PREFS_KEY_UTP_ENABLED, tr_sessionIsUTPEnabled( s ) );
1616    tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED, tr_sessionIsDHTEnabled( s ) );
1617    tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED, tr_sessionIsLPDEnabled( s ) );
1618    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT, tr_sessionGetPeerPort( s ) );
1619    tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, tr_sessionGetPeerPortRandomOnStart( s ) );
1620    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) );
1621    tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES, tr_sessionIsIncompleteFileNamingEnabled( s ) );
1622    tr_bencDictAddInt ( d, "rpc-version", RPC_VERSION );
1623    tr_bencDictAddInt ( d, "rpc-version-minimum", RPC_VERSION_MIN );
1624    tr_bencDictAddReal( d, "seedRatioLimit", tr_sessionGetRatioLimit( s ) );
1625    tr_bencDictAddBool( d, "seedRatioLimited", tr_sessionIsRatioLimited( s ) );
1626    tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT, tr_sessionGetIdleLimit( s ) );
1627    tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, tr_sessionIsIdleLimited( s ) );
1628    tr_bencDictAddBool( d, TR_PREFS_KEY_START, !tr_sessionGetPaused( s ) );
1629    tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL, tr_sessionGetDeleteSource( s ) );
1630    tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_UP ) );
1631    tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_UP ) );
1632    tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_DOWN ) );
1633    tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_DOWN ) );
1634    tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, tr_sessionGetTorrentDoneScript( s ) );
1635    tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, tr_sessionIsTorrentDoneScriptEnabled( s ) );
1636    tr_formatter_get_units( tr_bencDictAddDict( d, "units", 0 ) );
1637    tr_bencDictAddStr ( d, "version", LONG_VERSION_STRING );
1638    switch( tr_sessionGetEncryption( s ) ) {
1639        case TR_CLEAR_PREFERRED: str = "tolerated"; break;
1640        case TR_ENCRYPTION_REQUIRED: str = "required"; break;
1641        default: str = "preferred"; break;
1642    }
1643    tr_bencDictAddStr( d, TR_PREFS_KEY_ENCRYPTION, str );
1644
1645    return NULL;
1646}
1647
1648/***
1649****
1650***/
1651
1652static const char*
1653sessionClose( tr_session               * session,
1654              tr_benc                  * args_in UNUSED,
1655              tr_benc                  * args_out UNUSED,
1656              struct tr_rpc_idle_data  * idle_data UNUSED )
1657{
1658    notify( session, TR_RPC_SESSION_CLOSE, NULL );
1659    return NULL;
1660}
1661
1662/***
1663****
1664***/
1665
1666typedef const char* ( *handler )( tr_session*, tr_benc*, tr_benc*, struct tr_rpc_idle_data * );
1667
1668static struct method
1669{
1670    const char *  name;
1671    bool          immediate;
1672    handler       func;
1673}
1674methods[] =
1675{
1676    { "port-test",             false, portTest            },
1677    { "blocklist-update",      false, blocklistUpdate     },
1678    { "session-close",         true,  sessionClose        },
1679    { "session-get",           true,  sessionGet          },
1680    { "session-set",           true,  sessionSet          },
1681    { "session-stats",         true,  sessionStats        },
1682    { "torrent-add",           false, torrentAdd          },
1683    { "torrent-get",           true,  torrentGet          },
1684    { "torrent-remove",        true,  torrentRemove       },
1685    { "torrent-set",           true,  torrentSet          },
1686    { "torrent-set-location",  true,  torrentSetLocation  },
1687    { "torrent-start",         true,  torrentStart        },
1688    { "torrent-stop",          true,  torrentStop         },
1689    { "torrent-verify",        true,  torrentVerify       },
1690    { "torrent-reannounce",    true,  torrentReannounce   }
1691};
1692
1693static void
1694noop_response_callback( tr_session       * session UNUSED,
1695                        struct evbuffer  * response UNUSED,
1696                        void             * user_data UNUSED )
1697{
1698}
1699
1700static void
1701request_exec( tr_session             * session,
1702              tr_benc                * request,
1703              tr_rpc_response_func     callback,
1704              void                   * callback_user_data )
1705{
1706    int i;
1707    const char * str;
1708    tr_benc * args_in = tr_bencDictFind( request, "arguments" );
1709    const char * result = NULL;
1710
1711    if( callback == NULL )
1712        callback = noop_response_callback;
1713
1714    /* parse the request */
1715    if( !tr_bencDictFindStr( request, "method", &str ) )
1716        result = "no method name";
1717    else {
1718        const int n = TR_N_ELEMENTS( methods );
1719        for( i = 0; i < n; ++i )
1720            if( !strcmp( str, methods[i].name ) )
1721                break;
1722        if( i ==n )
1723            result = "method name not recognized";
1724    }
1725
1726    /* if we couldn't figure out which method to use, return an error */
1727    if( result != NULL )
1728    {
1729        int64_t tag;
1730        tr_benc response;
1731        struct evbuffer * buf;
1732
1733        tr_bencInitDict( &response, 3 );
1734        tr_bencDictAddDict( &response, "arguments", 0 );
1735        tr_bencDictAddStr( &response, "result", result );
1736        if( tr_bencDictFindInt( request, "tag", &tag ) )
1737            tr_bencDictAddInt( &response, "tag", tag );
1738
1739        buf = tr_bencToBuf( &response, TR_FMT_JSON_LEAN );
1740        (*callback)( session, buf, callback_user_data );
1741        evbuffer_free( buf );
1742
1743        tr_bencFree( &response );
1744    }
1745    else if( methods[i].immediate )
1746    {
1747        int64_t tag;
1748        tr_benc response;
1749        tr_benc * args_out;
1750        struct evbuffer * buf;
1751
1752        tr_bencInitDict( &response, 3 );
1753        args_out = tr_bencDictAddDict( &response, "arguments", 0 );
1754        result = (*methods[i].func)( session, args_in, args_out, NULL );
1755        if( result == NULL )
1756            result = "success";
1757        tr_bencDictAddStr( &response, "result", result );
1758        if( tr_bencDictFindInt( request, "tag", &tag ) )
1759            tr_bencDictAddInt( &response, "tag", tag );
1760
1761        buf = tr_bencToBuf( &response, TR_FMT_JSON_LEAN );
1762        (*callback)( session, buf, callback_user_data );
1763        evbuffer_free( buf );
1764
1765        tr_bencFree( &response );
1766    }
1767    else
1768    {
1769        int64_t tag;
1770        struct tr_rpc_idle_data * data = tr_new0( struct tr_rpc_idle_data, 1 );
1771        data->session = session;
1772        data->response = tr_new0( tr_benc, 1 );
1773        tr_bencInitDict( data->response, 3 );
1774        if( tr_bencDictFindInt( request, "tag", &tag ) )
1775            tr_bencDictAddInt( data->response, "tag", tag );
1776        data->args_out = tr_bencDictAddDict( data->response, "arguments", 0 );
1777        data->callback = callback;
1778        data->callback_user_data = callback_user_data;
1779        (*methods[i].func)( session, args_in, data->args_out, data );
1780    }
1781}
1782
1783void
1784tr_rpc_request_exec_json( tr_session            * session,
1785                          const void            * request_json,
1786                          int                     request_len,
1787                          tr_rpc_response_func    callback,
1788                          void                  * callback_user_data )
1789{
1790    tr_benc top;
1791    int have_content;
1792
1793    if( request_len < 0 )
1794        request_len = strlen( request_json );
1795
1796    have_content = !tr_jsonParse( "rpc", request_json, request_len, &top, NULL );
1797    request_exec( session, have_content ? &top : NULL, callback, callback_user_data );
1798
1799    if( have_content )
1800        tr_bencFree( &top );
1801}
1802
1803/**
1804 * Munge the URI into a usable form.
1805 *
1806 * We have very loose typing on this to make the URIs as simple as possible:
1807 * - anything not a 'tag' or 'method' is automatically in 'arguments'
1808 * - values that are all-digits are numbers
1809 * - values that are all-digits or commas are number lists
1810 * - all other values are strings
1811 */
1812void
1813tr_rpc_parse_list_str( tr_benc     * setme,
1814                       const char  * str,
1815                       int           len )
1816
1817{
1818    int valueCount;
1819    int * values = tr_parseNumberRange( str, len, &valueCount );
1820
1821    if( valueCount == 0 )
1822        tr_bencInitStr( setme, str, len );
1823    else if( valueCount == 1 )
1824        tr_bencInitInt( setme, values[0] );
1825    else {
1826        int i;
1827        tr_bencInitList( setme, valueCount );
1828        for( i=0; i<valueCount; ++i )
1829            tr_bencListAddInt( setme, values[i] );
1830    }
1831
1832    tr_free( values );
1833}
1834
1835void
1836tr_rpc_request_exec_uri( tr_session           * session,
1837                         const void           * request_uri,
1838                         int                    request_len,
1839                         tr_rpc_response_func   callback,
1840                         void                 * callback_user_data )
1841{
1842    tr_benc      top, * args;
1843    char *       request = tr_strndup( request_uri, request_len );
1844    const char * pch;
1845
1846    tr_bencInitDict( &top, 3 );
1847    args = tr_bencDictAddDict( &top, "arguments", 0 );
1848
1849    pch = strchr( request, '?' );
1850    if( !pch ) pch = request;
1851    while( pch )
1852    {
1853        const char * delim = strchr( pch, '=' );
1854        const char * next = strchr( pch, '&' );
1855        if( delim )
1856        {
1857            char *    key = tr_strndup( pch, delim - pch );
1858            int       isArg = strcmp( key, "method" ) && strcmp( key, "tag" );
1859            tr_benc * parent = isArg ? args : &top;
1860            tr_rpc_parse_list_str( tr_bencDictAdd( parent, key ),
1861                                  delim + 1,
1862                                  next ? (size_t)(
1863                                       next -
1864                                      ( delim + 1 ) ) : strlen( delim + 1 ) );
1865            tr_free( key );
1866        }
1867        pch = next ? next + 1 : NULL;
1868    }
1869
1870    request_exec( session, &top, callback, callback_user_data );
1871
1872    /* cleanup */
1873    tr_bencFree( &top );
1874    tr_free( request );
1875}
Note: See TracBrowser for help on using the repository browser.