source: trunk/libtransmission/rpcimpl.c @ 11834

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

(trunk) #3675 "Not all .part files are removed" -- handle trashing files via RPC.

When libtransmission gets a "remove torrent" request from RPC, it tries to delegate the work. This is because the GTK+ and Mac clients don't want torrents disappearing in a different thread and causing possible thread issues. So the GTK+ and Mac clients get notification about this via libtransmission's RPC callback and remove the torrents themselves. Unfortunately, that notification doesn't include information about whether or not to delete local data.

This commit adds that information to the RPC callback so that the Mac and GTK+ clients will know whether or not to trash the local files when a third-party RPC client requests that at torrent and its files be deleted.

  • Property svn:keywords set to Date Rev Author Id
File size: 65.2 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 11834 2011-02-06 17:30:46Z jordan $
11 */
12
13#include <assert.h>
14#include <ctype.h> /* isdigit */
15#include <errno.h>
16#include <stdlib.h> /* strtol */
17#include <string.h> /* strcmp */
18#include <unistd.h> /* unlink */
19
20#ifdef HAVE_ZLIB
21 #include <zlib.h>
22#endif
23
24#include <event2/buffer.h>
25
26#include "transmission.h"
27#include "bencode.h"
28#include "completion.h"
29#include "fdlimit.h"
30#include "json.h"
31#include "rpcimpl.h"
32#include "session.h"
33#include "stats.h"
34#include "torrent.h"
35#include "utils.h"
36#include "version.h"
37#include "web.h"
38
39#define RPC_VERSION     11
40#define RPC_VERSION_MIN 1
41
42#define RECENTLY_ACTIVE_SECONDS 60
43
44#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) )
45
46#if 0
47#define dbgmsg(fmt, ...) \
48    do { \
49        fprintf( stderr, "%s:%d"#fmt, __FILE__, __LINE__, __VA_ARGS__ ); \
50        fprintf( stderr, "\n" ); \
51    } while( 0 )
52#else
53#define dbgmsg( ... ) \
54    do { \
55        if( tr_deepLoggingIsActive( ) ) \
56            tr_deepLog( __FILE__, __LINE__, "RPC", __VA_ARGS__ ); \
57    } while( 0 )
58#endif
59
60
61/***
62****
63***/
64
65static tr_rpc_callback_status
66notify( tr_session * session,
67        int          type,
68        tr_torrent * tor )
69{
70    tr_rpc_callback_status status = 0;
71
72    if( session->rpc_func )
73        status = session->rpc_func( session, type, tor,
74                                    session->rpc_func_user_data );
75
76    return status;
77}
78
79/***
80****
81***/
82
83/* For functions that can't be immediately executed, like torrentAdd,
84 * this is the callback data used to pass a response to the caller
85 * when the task is complete */
86struct tr_rpc_idle_data
87{
88    tr_session            * session;
89    tr_benc               * response;
90    tr_benc               * args_out;
91    tr_rpc_response_func    callback;
92    void                  * callback_user_data;
93};
94
95static void
96tr_idle_function_done( struct tr_rpc_idle_data * data, const char * result )
97{
98    struct evbuffer * buf = evbuffer_new( );
99
100    if( result == NULL )
101        result = "success";
102    tr_bencDictAddStr( data->response, "result", result );
103
104    tr_bencToBuf( data->response, TR_FMT_JSON_LEAN, buf );
105    (*data->callback)( data->session, buf, data->callback_user_data );
106
107    evbuffer_free( buf );
108    tr_bencFree( data->response );
109    tr_free( data->response );
110    tr_free( data );
111}
112
113/***
114****
115***/
116
117static tr_torrent **
118getTorrents( tr_session * session,
119             tr_benc    * args,
120             int        * setmeCount )
121{
122    int           torrentCount = 0;
123    int64_t       id;
124    tr_torrent ** torrents = NULL;
125    tr_benc *     ids;
126    const char * str;
127
128    if( tr_bencDictFindList( args, "ids", &ids ) )
129    {
130        int       i;
131        const int n = tr_bencListSize( ids );
132
133        torrents = tr_new0( tr_torrent *, n );
134
135        for( i = 0; i < n; ++i )
136        {
137            tr_torrent * tor = NULL;
138            tr_benc *    node = tr_bencListChild( ids, i );
139            const char * str;
140            if( tr_bencGetInt( node, &id ) )
141                tor = tr_torrentFindFromId( session, id );
142            else if( tr_bencGetStr( node, &str ) )
143                tor = tr_torrentFindFromHashString( session, str );
144            if( tor )
145                torrents[torrentCount++] = tor;
146        }
147    }
148    else if( tr_bencDictFindInt( args, "ids", &id )
149           || tr_bencDictFindInt( args, "id", &id ) )
150    {
151        tr_torrent * tor;
152        torrents = tr_new0( tr_torrent *, 1 );
153        if( ( tor = tr_torrentFindFromId( session, id ) ) )
154            torrents[torrentCount++] = tor;
155    }
156    else if( tr_bencDictFindStr( args, "ids", &str ) )
157    {
158        if( !strcmp( str, "recently-active" ) )
159        {
160            tr_torrent * tor = NULL;
161            const time_t now = tr_time( );
162            const time_t window = RECENTLY_ACTIVE_SECONDS;
163            const int n = tr_sessionCountTorrents( session );
164            torrents = tr_new0( tr_torrent *, n );
165            while( ( tor = tr_torrentNext( session, tor ) ) )
166                if( tor->anyDate >= now - window )
167                    torrents[torrentCount++] = tor;
168        }
169        else
170        {
171            tr_torrent * tor;
172            torrents = tr_new0( tr_torrent *, 1 );
173            if(( tor = tr_torrentFindFromHashString( session, str )))
174                torrents[torrentCount++] = tor;
175        }
176    }
177    else /* all of them */
178    {
179        tr_torrent * tor = NULL;
180        const int n = tr_sessionCountTorrents( session );
181        torrents = tr_new0( tr_torrent *, n );
182        while( ( tor = tr_torrentNext( session, tor ) ) )
183            torrents[torrentCount++] = tor;
184    }
185
186    *setmeCount = torrentCount;
187    return torrents;
188}
189
190static const char*
191torrentStart( tr_session               * session,
192              tr_benc                  * args_in,
193              tr_benc                  * args_out UNUSED,
194              struct tr_rpc_idle_data  * idle_data UNUSED )
195{
196    int           i, torrentCount;
197    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
198
199    assert( idle_data == NULL );
200
201    for( i = 0; i < torrentCount; ++i )
202    {
203        tr_torrent * tor = torrents[i];
204        if( !tor->isRunning )
205        {
206            tr_torrentStart( tor );
207            notify( session, TR_RPC_TORRENT_STARTED, tor );
208        }
209    }
210    tr_free( torrents );
211    return NULL;
212}
213
214static const char*
215torrentStop( tr_session               * session,
216             tr_benc                  * args_in,
217             tr_benc                  * args_out UNUSED,
218             struct tr_rpc_idle_data  * idle_data UNUSED )
219{
220    int           i, torrentCount;
221    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
222
223    assert( idle_data == NULL );
224
225    for( i = 0; i < torrentCount; ++i )
226    {
227        tr_torrent * tor = torrents[i];
228
229        if( tor->isRunning )
230        {
231            tor->isStopping = TRUE;
232            notify( session, TR_RPC_TORRENT_STOPPED, tor );
233        }
234    }
235    tr_free( torrents );
236    return NULL;
237}
238
239static const char*
240torrentRemove( tr_session               * session,
241               tr_benc                  * args_in,
242               tr_benc                  * args_out UNUSED,
243               struct tr_rpc_idle_data  * idle_data UNUSED )
244{
245    int i;
246    int torrentCount;
247    tr_rpc_callback_type type;
248    tr_bool deleteFlag = FALSE;
249    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
250
251    assert( idle_data == NULL );
252
253    tr_bencDictFindBool( args_in, "delete-local-data", &deleteFlag );
254    type = deleteFlag ? TR_RPC_TORRENT_TRASHING
255                      : TR_RPC_TORRENT_REMOVING;
256
257    for( i=0; i<torrentCount; ++i )
258    {
259        tr_torrent * tor = torrents[i];
260        const tr_rpc_callback_status status = notify( session, type, tor );
261        if( !( status & TR_RPC_NOREMOVE ) )
262            tr_torrentRemove( tor, deleteFlag, NULL );
263    }
264
265    tr_free( torrents );
266    return NULL;
267}
268
269static const char*
270torrentReannounce( tr_session               * session,
271                   tr_benc                  * args_in,
272                   tr_benc                  * args_out UNUSED,
273                   struct tr_rpc_idle_data  * idle_data UNUSED )
274{
275    int i, torrentCount;
276    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
277
278    assert( idle_data == NULL );
279
280    for( i=0; i<torrentCount; ++i )
281    {
282        tr_torrent * tor = torrents[i];
283        if( tr_torrentCanManualUpdate( tor ) )
284        {
285            tr_torrentManualUpdate( tor );
286            notify( session, TR_RPC_TORRENT_CHANGED, tor );
287        }
288    }
289
290    tr_free( torrents );
291    return NULL;
292}
293
294static const char*
295torrentVerify( tr_session               * session,
296               tr_benc                  * args_in,
297               tr_benc                  * args_out UNUSED,
298               struct tr_rpc_idle_data  * idle_data UNUSED )
299{
300    int           i, torrentCount;
301    tr_torrent ** torrents = getTorrents( session, args_in, &torrentCount );
302
303    assert( idle_data == NULL );
304
305    for( i = 0; i < torrentCount; ++i )
306    {
307        tr_torrent * tor = torrents[i];
308        tr_torrentVerify( tor );
309        notify( session, TR_RPC_TORRENT_CHANGED, tor );
310    }
311
312    tr_free( torrents );
313    return NULL;
314}
315
316/***
317****
318***/
319
320static void
321addFileStats( const tr_torrent * tor, tr_benc * list )
322{
323    tr_file_index_t i;
324    tr_file_index_t n;
325    const tr_info * info = tr_torrentInfo( tor );
326    tr_file_stat * files = tr_torrentFiles( tor, &n );
327
328    for( i = 0; i < info->fileCount; ++i )
329    {
330        const tr_file * file = &info->files[i];
331        tr_benc * d = tr_bencListAddDict( list, 3 );
332        tr_bencDictAddInt( d, "bytesCompleted", files[i].bytesCompleted );
333        tr_bencDictAddInt( d, "priority", file->priority );
334        tr_bencDictAddBool( d, "wanted", !file->dnd );
335    }
336
337    tr_torrentFilesFree( files, n );
338}
339
340static void
341addFiles( const tr_torrent * tor,
342          tr_benc *          list )
343{
344    tr_file_index_t i;
345    tr_file_index_t n;
346    const tr_info * info = tr_torrentInfo( tor );
347    tr_file_stat *  files = tr_torrentFiles( tor, &n );
348
349    for( i = 0; i < info->fileCount; ++i )
350    {
351        const tr_file * file = &info->files[i];
352        tr_benc *       d = tr_bencListAddDict( list, 3 );
353        tr_bencDictAddInt( d, "bytesCompleted", files[i].bytesCompleted );
354        tr_bencDictAddInt( d, "length", file->length );
355        tr_bencDictAddStr( d, "name", file->name );
356    }
357
358    tr_torrentFilesFree( files, n );
359}
360
361static void
362addWebseeds( const tr_info * info,
363             tr_benc *       webseeds )
364{
365    int i;
366
367    for( i = 0; i < info->webseedCount; ++i )
368        tr_bencListAddStr( webseeds, info->webseeds[i] );
369}
370
371static void
372addTrackers( const tr_info * info,
373             tr_benc *       trackers )
374{
375    int i;
376
377    for( i = 0; i < info->trackerCount; ++i )
378    {
379        const tr_tracker_info * t = &info->trackers[i];
380        tr_benc *               d = tr_bencListAddDict( trackers, 4 );
381        tr_bencDictAddStr( d, "announce", t->announce );
382        tr_bencDictAddInt( d, "id", t->id );
383        tr_bencDictAddStr( d, "scrape", t->scrape );
384        tr_bencDictAddInt( d, "tier", t->tier );
385    }
386}
387
388static void
389addTrackerStats( const tr_tracker_stat * st, int n, tr_benc * list )
390{
391    int i;
392
393    for( i=0; i<n; ++i )
394    {
395        const tr_tracker_stat * s = &st[i];
396        tr_benc * d = tr_bencListAddDict( list, 26 );
397        tr_bencDictAddStr ( d, "announce", s->announce );
398        tr_bencDictAddInt ( d, "announceState", s->announceState );
399        tr_bencDictAddInt ( d, "downloadCount", s->downloadCount );
400        tr_bencDictAddBool( d, "hasAnnounced", s->hasAnnounced );
401        tr_bencDictAddBool( d, "hasScraped", s->hasScraped );
402        tr_bencDictAddStr ( d, "host", s->host );
403        tr_bencDictAddInt ( d, "id", s->id );
404        tr_bencDictAddBool( d, "isBackup", s->isBackup );
405        tr_bencDictAddInt ( d, "lastAnnouncePeerCount", s->lastAnnouncePeerCount );
406        tr_bencDictAddStr ( d, "lastAnnounceResult", s->lastAnnounceResult );
407        tr_bencDictAddInt ( d, "lastAnnounceStartTime", s->lastAnnounceStartTime );
408        tr_bencDictAddBool( d, "lastAnnounceSucceeded", s->lastAnnounceSucceeded );
409        tr_bencDictAddInt ( d, "lastAnnounceTime", s->lastAnnounceTime );
410        tr_bencDictAddBool( d, "lastAnnounceTimedOut", s->lastAnnounceTimedOut );
411        tr_bencDictAddStr ( d, "lastScrapeResult", s->lastScrapeResult );
412        tr_bencDictAddInt ( d, "lastScrapeStartTime", s->lastScrapeStartTime );
413        tr_bencDictAddBool( d, "lastScrapeSucceeded", s->lastScrapeSucceeded );
414        tr_bencDictAddInt ( d, "lastScrapeTime", s->lastScrapeTime );
415        tr_bencDictAddInt ( d, "lastScrapeTimedOut", s->lastScrapeTimedOut );
416        tr_bencDictAddInt ( d, "leecherCount", s->leecherCount );
417        tr_bencDictAddInt ( d, "nextAnnounceTime", s->nextAnnounceTime );
418        tr_bencDictAddInt ( d, "nextScrapeTime", s->nextScrapeTime );
419        tr_bencDictAddStr ( d, "scrape", s->scrape );
420        tr_bencDictAddInt ( d, "scrapeState", s->scrapeState );
421        tr_bencDictAddInt ( d, "seederCount", s->seederCount );
422        tr_bencDictAddInt ( d, "tier", s->tier );
423    }
424}
425
426static void
427addPeers( const tr_torrent * tor,
428          tr_benc *          list )
429{
430    int            i;
431    int            peerCount;
432    tr_peer_stat * peers = tr_torrentPeers( tor, &peerCount );
433
434    tr_bencInitList( list, peerCount );
435
436    for( i = 0; i < peerCount; ++i )
437    {
438        tr_benc *            d = tr_bencListAddDict( list, 14 );
439        const tr_peer_stat * peer = peers + i;
440        tr_bencDictAddStr ( d, "address", peer->addr );
441        tr_bencDictAddStr ( d, "clientName", peer->client );
442        tr_bencDictAddBool( d, "clientIsChoked", peer->clientIsChoked );
443        tr_bencDictAddBool( d, "clientIsInterested", peer->clientIsInterested );
444        tr_bencDictAddStr ( d, "flagStr", peer->flagStr );
445        tr_bencDictAddBool( d, "isDownloadingFrom", peer->isDownloadingFrom );
446        tr_bencDictAddBool( d, "isEncrypted", peer->isEncrypted );
447        tr_bencDictAddBool( d, "isIncoming", peer->isIncoming );
448        tr_bencDictAddBool( d, "isUploadingTo", peer->isUploadingTo );
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, inf->name );
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, "peersKnown" ) )
559        tr_bencDictAddInt( d, key, st->peersKnown );
560    else if( tr_streq( key, keylen, "peersSendingToUs" ) )
561        tr_bencDictAddInt( d, key, st->peersSendingToUs );
562    else if( tr_streq( key, keylen, "pieces" ) ) {
563        const tr_bitfield * pieces = tr_cpPieceBitfield( &tor->completion );
564        char * str = tr_base64_encode( pieces->bits, pieces->byteCount, NULL );
565        tr_bencDictAddStr( d, key, str!=NULL ? str : "" );
566        tr_free( str );
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 tr_bool
772findAnnounceUrl( const tr_tracker_info * t, int n, const char * url, int * pos )
773{
774    int i;
775    tr_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    tr_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_urlIsValid( announce, -1 )
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    tr_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    tr_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        tr_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        tr_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            long               response_code,
1070            const void       * response,
1071            size_t             response_byte_count,
1072            void             * user_data )
1073{
1074    char result[1024];
1075    struct tr_rpc_idle_data * data = user_data;
1076
1077    if( response_code != 200 )
1078    {
1079        tr_snprintf( result, sizeof( result ), "portTested: http error %ld: %s",
1080                     response_code, tr_webGetResponseStr( response_code ) );
1081    }
1082    else /* success */
1083    {
1084        const tr_bool isOpen = response_byte_count && *(char*)response == '1';
1085        tr_bencDictAddBool( data->args_out, "port-is-open", isOpen );
1086        tr_snprintf( result, sizeof( result ), "success" );
1087    }
1088
1089    tr_idle_function_done( data, result );
1090}
1091
1092static const char*
1093portTest( tr_session               * session,
1094          tr_benc                  * args_in UNUSED,
1095          tr_benc                  * args_out UNUSED,
1096          struct tr_rpc_idle_data  * idle_data )
1097{
1098    const int port = tr_sessionGetPeerPort( session );
1099    char * url = tr_strdup_printf( "http://portcheck.transmissionbt.com/%d", port );
1100    tr_webRun( session, url, NULL, portTested, idle_data );
1101    tr_free( url );
1102    return NULL;
1103}
1104
1105/***
1106****
1107***/
1108
1109static void
1110gotNewBlocklist( tr_session       * session,
1111                 long               response_code,
1112                 const void       * response,
1113                 size_t             response_byte_count,
1114                 void             * user_data )
1115{
1116    char result[1024];
1117    struct tr_rpc_idle_data * data = user_data;
1118
1119    if( response_code != 200 )
1120    {
1121        tr_snprintf( result, sizeof( result ), "gotNewBlocklist: http error %ld: %s",
1122                     response_code, tr_webGetResponseStr( response_code ) );
1123    }
1124    else /* successfully fetched the blocklist... */
1125    {
1126        int fd;
1127        const char * configDir = tr_sessionGetConfigDir( session );
1128        char * filename = tr_buildPath( configDir, "blocklist.tmp", NULL );
1129
1130        errno = 0;
1131
1132        if( !errno ) {
1133            fd = tr_open_file_for_writing( filename );
1134            if( fd < 0 )
1135                tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename, tr_strerror( errno ) );
1136        }
1137
1138        if( !errno ) {
1139            const char * buf = response;
1140            size_t buflen = response_byte_count;
1141            while( buflen > 0 ) {
1142                int n = write( fd, buf, buflen );
1143                if( n < 0 ) {
1144                    tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename, tr_strerror( errno ) );
1145                    break;
1146                }
1147                buf += n;
1148                buflen -= n;
1149            }
1150            tr_close_file( fd );
1151        }
1152
1153#ifdef HAVE_ZLIB
1154        if( !errno )
1155        {
1156            char * filename2 = tr_buildPath( configDir, "blocklist.txt.tmp", NULL );
1157            fd = tr_open_file_for_writing( filename2 );
1158            if( fd < 0 )
1159                tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename2, tr_strerror( errno ) );
1160            else {
1161                gzFile gzf = gzopen( filename, "r" );
1162                if( gzf ) {
1163                    const size_t buflen = 1024 * 128; /* 128 KiB buffer */
1164                    uint8_t * buf = tr_valloc( buflen );
1165                    for( ;; ) {
1166                        int n = gzread( gzf, buf, buflen );
1167                        if( n < 0 ) /* error */
1168                            tr_snprintf( result, sizeof( result ), _( "Error reading \"%1$s\": %2$s" ), filename, gzerror( gzf, NULL ) );
1169                        if( n < 1 ) /* error or EOF */
1170                            break;
1171                        if( write( fd, buf, n ) < 0 )
1172                            tr_snprintf( result, sizeof( result ), _( "Couldn't save file \"%1$s\": %2$s" ), filename2, tr_strerror( errno ) );
1173                    }
1174                    tr_free( buf );
1175                    gzclose( gzf );
1176                }
1177                tr_close_file( fd );
1178            }
1179
1180            unlink( filename );
1181            tr_free( filename );
1182            filename = filename2;
1183        }
1184#endif
1185
1186        if( !errno ) {
1187            /* feed it to the session and give the client a response */
1188            const int rule_count = tr_blocklistSetContent( session, filename );
1189            tr_bencDictAddInt( data->args_out, "blocklist-size", rule_count );
1190            tr_snprintf( result, sizeof( result ), "success" );
1191        }
1192
1193        unlink( filename );
1194        tr_free( filename );
1195    }
1196
1197    tr_idle_function_done( data, result );
1198}
1199
1200static const char*
1201blocklistUpdate( tr_session               * session,
1202                 tr_benc                  * args_in UNUSED,
1203                 tr_benc                  * args_out UNUSED,
1204                 struct tr_rpc_idle_data  * idle_data )
1205{
1206    tr_webRun( session, session->blocklist_url, NULL, gotNewBlocklist, idle_data );
1207    return NULL;
1208}
1209
1210/***
1211****
1212***/
1213
1214static void
1215addTorrentImpl( struct tr_rpc_idle_data * data, tr_ctor * ctor )
1216{
1217    int err = 0;
1218    const char * result = NULL;
1219    tr_torrent * tor = tr_torrentNew( ctor, &err );
1220
1221    tr_ctorFree( ctor );
1222
1223    if( tor )
1224    {
1225        tr_benc fields;
1226        tr_bencInitList( &fields, 3 );
1227        tr_bencListAddStr( &fields, "id" );
1228        tr_bencListAddStr( &fields, "name" );
1229        tr_bencListAddStr( &fields, "hashString" );
1230        addInfo( tor, tr_bencDictAdd( data->args_out, "torrent-added" ), &fields );
1231        notify( data->session, TR_RPC_TORRENT_ADDED, tor );
1232        tr_bencFree( &fields );
1233    }
1234    else if( err == TR_PARSE_DUPLICATE )
1235    {
1236        result = "duplicate torrent";
1237    }
1238    else if( err == TR_PARSE_ERR )
1239    {
1240        result = "invalid or corrupt torrent file";
1241    }
1242
1243    tr_idle_function_done( data, result );
1244}
1245
1246
1247struct add_torrent_idle_data
1248{
1249    struct tr_rpc_idle_data * data;
1250    tr_ctor * ctor;
1251};
1252
1253static void
1254gotMetadataFromURL( tr_session       * session UNUSED,
1255                    long               response_code,
1256                    const void       * response,
1257                    size_t             response_byte_count,
1258                    void             * user_data )
1259{
1260    struct add_torrent_idle_data * data = user_data;
1261
1262    dbgmsg( "torrentAdd: HTTP response code was %ld (%s); response length was %zu bytes",
1263            response_code, tr_webGetResponseStr( response_code ), response_byte_count );
1264
1265    if( response_code==200 || response_code==221 ) /* http or ftp success.. */
1266    {
1267        tr_ctorSetMetainfo( data->ctor, response, response_byte_count );
1268        addTorrentImpl( data->data, data->ctor );
1269    }
1270    else
1271    {
1272        char result[1024];
1273        tr_snprintf( result, sizeof( result ), "gotMetadataFromURL: http error %ld: %s",
1274                     response_code, tr_webGetResponseStr( response_code ) );
1275        tr_idle_function_done( data->data, result );
1276    }
1277
1278    tr_free( data );
1279}
1280
1281static tr_bool
1282isCurlURL( const char * filename )
1283{
1284    if( filename == NULL )
1285        return FALSE;
1286
1287    return !strncmp( filename, "ftp://", 6 ) ||
1288           !strncmp( filename, "http://", 7 ) ||
1289           !strncmp( filename, "https://", 8 );
1290}
1291
1292static tr_file_index_t*
1293fileListFromList( tr_benc * list, tr_file_index_t * setmeCount )
1294{
1295    size_t i;
1296    const size_t childCount = tr_bencListSize( list );
1297    tr_file_index_t n = 0;
1298    tr_file_index_t * files = tr_new0( tr_file_index_t, childCount );
1299
1300    for( i=0; i<childCount; ++i ) {
1301        int64_t intVal;
1302        if( tr_bencGetInt( tr_bencListChild( list, i ), &intVal ) )
1303            files[n++] = (tr_file_index_t)intVal;
1304    }
1305
1306    *setmeCount = n;
1307    return files;
1308}
1309
1310static const char*
1311torrentAdd( tr_session               * session,
1312            tr_benc                  * args_in,
1313            tr_benc                  * args_out UNUSED,
1314            struct tr_rpc_idle_data  * idle_data )
1315{
1316    const char * filename = NULL;
1317    const char * metainfo_base64 = NULL;
1318
1319    assert( idle_data != NULL );
1320
1321    tr_bencDictFindStr( args_in, "filename", &filename );
1322    tr_bencDictFindStr( args_in, "metainfo", &metainfo_base64 );
1323    if( !filename && !metainfo_base64 )
1324        return "no filename or metainfo specified";
1325    else
1326    {
1327        int64_t      i;
1328        tr_bool      boolVal;
1329        const char * str;
1330        tr_benc    * l;
1331        tr_ctor    * ctor = tr_ctorNew( session );
1332
1333        /* set the optional arguments */
1334
1335        if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1336            tr_ctorSetDownloadDir( ctor, TR_FORCE, str );
1337
1338        if( tr_bencDictFindBool( args_in, "paused", &boolVal ) )
1339            tr_ctorSetPaused( ctor, TR_FORCE, boolVal );
1340
1341        if( tr_bencDictFindInt( args_in, "peer-limit", &i ) )
1342            tr_ctorSetPeerLimit( ctor, TR_FORCE, i );
1343
1344        if( tr_bencDictFindInt( args_in, "bandwidthPriority", &i ) )
1345            tr_ctorSetBandwidthPriority( ctor, i );
1346
1347        if( tr_bencDictFindList( args_in, "files-unwanted", &l ) ) {
1348            tr_file_index_t fileCount;
1349            tr_file_index_t * files = fileListFromList( l, &fileCount );
1350            tr_ctorSetFilesWanted( ctor, files, fileCount, FALSE );
1351            tr_free( files );
1352        }
1353        if( tr_bencDictFindList( args_in, "files-wanted", &l ) ) {
1354            tr_file_index_t fileCount;
1355            tr_file_index_t * files = fileListFromList( l, &fileCount );
1356            tr_ctorSetFilesWanted( ctor, files, fileCount, TRUE );
1357            tr_free( files );
1358        }
1359
1360        if( tr_bencDictFindList( args_in, "priority-low", &l ) ) {
1361            tr_file_index_t fileCount;
1362            tr_file_index_t * files = fileListFromList( l, &fileCount );
1363            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_LOW );
1364            tr_free( files );
1365        }
1366        if( tr_bencDictFindList( args_in, "priority-normal", &l ) ) {
1367            tr_file_index_t fileCount;
1368            tr_file_index_t * files = fileListFromList( l, &fileCount );
1369            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_NORMAL );
1370            tr_free( files );
1371        }
1372        if( tr_bencDictFindList( args_in, "priority-high", &l ) ) {
1373            tr_file_index_t fileCount;
1374            tr_file_index_t * files = fileListFromList( l, &fileCount );
1375            tr_ctorSetFilePriorities( ctor, files, fileCount, TR_PRI_HIGH );
1376            tr_free( files );
1377        }
1378
1379        dbgmsg( "torrentAdd: filename is \"%s\"", filename ? filename : "(null)" );
1380
1381        if( isCurlURL( filename ) )
1382        {
1383            struct add_torrent_idle_data * d = tr_new0( struct add_torrent_idle_data, 1 );
1384            d->data = idle_data;
1385            d->ctor = ctor;
1386            tr_webRun( session, filename, NULL, gotMetadataFromURL, d );
1387        }
1388        else
1389        {
1390            char * fname = tr_strstrip( tr_strdup( filename ) );
1391
1392            if( fname == NULL )
1393            {
1394                int len;
1395                char * metainfo = tr_base64_decode( metainfo_base64, -1, &len );
1396                tr_ctorSetMetainfo( ctor, (uint8_t*)metainfo, len );
1397                tr_free( metainfo );
1398            }
1399            else if( !strncmp( fname, "magnet:?", 8 ) )
1400            {
1401                tr_ctorSetMetainfoFromMagnetLink( ctor, fname );
1402            }
1403            else
1404            {
1405                tr_ctorSetMetainfoFromFile( ctor, fname );
1406            }
1407
1408            addTorrentImpl( idle_data, ctor );
1409
1410            tr_free( fname );
1411        }
1412
1413    }
1414
1415    return NULL;
1416}
1417
1418/***
1419****
1420***/
1421
1422static const char*
1423sessionSet( tr_session               * session,
1424            tr_benc                  * args_in,
1425            tr_benc                  * args_out UNUSED,
1426            struct tr_rpc_idle_data  * idle_data UNUSED )
1427{
1428    int64_t      i;
1429    double       d;
1430    tr_bool      boolVal;
1431    const char * str;
1432
1433    assert( idle_data == NULL );
1434
1435    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, &i ) )
1436        tr_sessionSetCacheLimit_MB( session, i );
1437    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_UP_KBps, &i ) )
1438        tr_sessionSetAltSpeed_KBps( session, TR_UP, i );
1439    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, &i ) )
1440        tr_sessionSetAltSpeed_KBps( session, TR_DOWN, i );
1441    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_ENABLED, &boolVal ) )
1442        tr_sessionUseAltSpeed( session, boolVal );
1443    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &i ) )
1444        tr_sessionSetAltSpeedBegin( session, i );
1445    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_END, &i ) )
1446        tr_sessionSetAltSpeedEnd( session, i );
1447    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &i ) )
1448        tr_sessionSetAltSpeedDay( session, i );
1449    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &boolVal ) )
1450        tr_sessionUseAltSpeedTime( session, boolVal );
1451    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_BLOCKLIST_ENABLED, &boolVal ) )
1452        tr_blocklistSetEnabled( session, boolVal );
1453    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_BLOCKLIST_URL, &str ) )
1454        tr_blocklistSetURL( session, str );
1455    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1456        tr_sessionSetDownloadDir( session, str );
1457    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_INCOMPLETE_DIR, &str ) )
1458        tr_sessionSetIncompleteDir( session, str );
1459    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, &boolVal ) )
1460        tr_sessionSetIncompleteDirEnabled( session, boolVal );
1461    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &i ) )
1462        tr_sessionSetPeerLimit( session, i );
1463    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i ) )
1464        tr_sessionSetPeerLimitPerTorrent( session, i );
1465    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) )
1466        tr_sessionSetPexEnabled( session, boolVal );
1467    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DHT_ENABLED, &boolVal ) )
1468        tr_sessionSetDHTEnabled( session, boolVal );
1469    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_LPD_ENABLED, &boolVal ) )
1470        tr_sessionSetLPDEnabled( session, boolVal );
1471    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, &boolVal ) )
1472        tr_sessionSetPeerPortRandomOnStart( session, boolVal );
1473    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_PEER_PORT, &i ) )
1474        tr_sessionSetPeerPort( session, i );
1475    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) )
1476        tr_sessionSetPortForwardingEnabled( session, boolVal );
1477    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_RENAME_PARTIAL_FILES, &boolVal ) )
1478        tr_sessionSetIncompleteFileNamingEnabled( session, boolVal );
1479    if( tr_bencDictFindReal( args_in, "seedRatioLimit", &d ) )
1480        tr_sessionSetRatioLimit( session, d );
1481    if( tr_bencDictFindBool( args_in, "seedRatioLimited", &boolVal ) )
1482        tr_sessionSetRatioLimited( session, boolVal );
1483    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_IDLE_LIMIT, &i ) )
1484        tr_sessionSetIdleLimit( session, i );
1485    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, &boolVal ) )
1486        tr_sessionSetIdleLimited( session, boolVal );
1487    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_START, &boolVal ) )
1488        tr_sessionSetPaused( session, !boolVal );
1489    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, &str ) )
1490        tr_sessionSetTorrentDoneScript( session, str );
1491    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, &boolVal ) )
1492        tr_sessionSetTorrentDoneScriptEnabled( session, boolVal );
1493    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_TRASH_ORIGINAL, &boolVal ) )
1494        tr_sessionSetDeleteSource( session, boolVal );
1495    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_DSPEED_KBps, &i ) )
1496        tr_sessionSetSpeedLimit_KBps( session, TR_DOWN, i );
1497    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_DSPEED_ENABLED, &boolVal ) )
1498        tr_sessionLimitSpeed( session, TR_DOWN, boolVal );
1499    if( tr_bencDictFindInt( args_in, TR_PREFS_KEY_USPEED_KBps, &i ) )
1500        tr_sessionSetSpeedLimit_KBps( session, TR_UP, i );
1501    if( tr_bencDictFindBool( args_in, TR_PREFS_KEY_USPEED_ENABLED, &boolVal ) )
1502        tr_sessionLimitSpeed( session, TR_UP, boolVal );
1503    if( tr_bencDictFindStr( args_in, TR_PREFS_KEY_ENCRYPTION, &str ) ) {
1504        if( !strcmp( str, "required" ) )
1505            tr_sessionSetEncryption( session, TR_ENCRYPTION_REQUIRED );
1506        else if( !strcmp( str, "tolerated" ) )
1507            tr_sessionSetEncryption( session, TR_CLEAR_PREFERRED );
1508        else
1509            tr_sessionSetEncryption( session, TR_ENCRYPTION_PREFERRED );
1510    }
1511
1512    notify( session, TR_RPC_SESSION_CHANGED, NULL );
1513
1514    return NULL;
1515}
1516
1517static const char*
1518sessionStats( tr_session               * session,
1519              tr_benc                  * args_in UNUSED,
1520              tr_benc                  * args_out,
1521              struct tr_rpc_idle_data  * idle_data UNUSED )
1522{
1523    int running = 0;
1524    int total = 0;
1525    tr_benc * d;
1526    tr_session_stats currentStats = { 0.0f, 0, 0, 0, 0, 0 };
1527    tr_session_stats cumulativeStats = { 0.0f, 0, 0, 0, 0, 0 };
1528    tr_torrent * tor = NULL;
1529
1530    assert( idle_data == NULL );
1531
1532    while(( tor = tr_torrentNext( session, tor ))) {
1533        ++total;
1534        if( tor->isRunning )
1535            ++running;
1536    }
1537
1538    tr_sessionGetStats( session, &currentStats );
1539    tr_sessionGetCumulativeStats( session, &cumulativeStats );
1540
1541    tr_bencDictAddInt ( args_out, "activeTorrentCount", running );
1542    tr_bencDictAddReal( args_out, "downloadSpeed", tr_sessionGetPieceSpeed_Bps( session, TR_DOWN ) );
1543    tr_bencDictAddInt ( args_out, "pausedTorrentCount", total - running );
1544    tr_bencDictAddInt ( args_out, "torrentCount", total );
1545    tr_bencDictAddReal( args_out, "uploadSpeed", tr_sessionGetPieceSpeed_Bps( session, TR_UP ) );
1546
1547    d = tr_bencDictAddDict( args_out, "cumulative-stats", 5 );
1548    tr_bencDictAddInt( d, "downloadedBytes", cumulativeStats.downloadedBytes );
1549    tr_bencDictAddInt( d, "filesAdded", cumulativeStats.filesAdded );
1550    tr_bencDictAddInt( d, "secondsActive", cumulativeStats.secondsActive );
1551    tr_bencDictAddInt( d, "sessionCount", cumulativeStats.sessionCount );
1552    tr_bencDictAddInt( d, "uploadedBytes", cumulativeStats.uploadedBytes );
1553
1554    d = tr_bencDictAddDict( args_out, "current-stats", 5 );
1555    tr_bencDictAddInt( d, "downloadedBytes", currentStats.downloadedBytes );
1556    tr_bencDictAddInt( d, "filesAdded", currentStats.filesAdded );
1557    tr_bencDictAddInt( d, "secondsActive", currentStats.secondsActive );
1558    tr_bencDictAddInt( d, "sessionCount", currentStats.sessionCount );
1559    tr_bencDictAddInt( d, "uploadedBytes", currentStats.uploadedBytes );
1560
1561    return NULL;
1562}
1563
1564static const char*
1565sessionGet( tr_session               * s,
1566            tr_benc                  * args_in UNUSED,
1567            tr_benc                  * args_out,
1568            struct tr_rpc_idle_data  * idle_data UNUSED )
1569{
1570    const char * str;
1571    tr_benc * d = args_out;
1572
1573    assert( idle_data == NULL );
1574    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP_KBps, tr_sessionGetAltSpeed_KBps(s,TR_UP) );
1575    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, tr_sessionGetAltSpeed_KBps(s,TR_DOWN) );
1576    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED, tr_sessionUsesAltSpeed(s) );
1577    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, tr_sessionGetAltSpeedBegin(s) );
1578    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,tr_sessionGetAltSpeedEnd(s) );
1579    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,tr_sessionGetAltSpeedDay(s) );
1580    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, tr_sessionUsesAltSpeedTime(s) );
1581    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, tr_blocklistIsEnabled( s ) );
1582    tr_bencDictAddStr ( d, TR_PREFS_KEY_BLOCKLIST_URL, tr_blocklistGetURL( s ) );
1583    tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, tr_sessionGetCacheLimit_MB( s ) );
1584    tr_bencDictAddInt ( d, "blocklist-size", tr_blocklistGetRuleCount( s ) );
1585    tr_bencDictAddStr ( d, "config-dir", tr_sessionGetConfigDir( s ) );
1586    tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR, tr_sessionGetDownloadDir( s ) );
1587    tr_bencDictAddInt ( d, "download-dir-free-space",  tr_sessionGetDownloadDirFreeSpace( s ) );
1588    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, tr_sessionGetPeerLimit( s ) );
1589    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT, tr_sessionGetPeerLimitPerTorrent( s ) );
1590    tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR, tr_sessionGetIncompleteDir( s ) );
1591    tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, tr_sessionIsIncompleteDirEnabled( s ) );
1592    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED, tr_sessionIsPexEnabled( s ) );
1593    tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED, tr_sessionIsDHTEnabled( s ) );
1594    tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED, tr_sessionIsLPDEnabled( s ) );
1595    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT, tr_sessionGetPeerPort( s ) );
1596    tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, tr_sessionGetPeerPortRandomOnStart( s ) );
1597    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, tr_sessionIsPortForwardingEnabled( s ) );
1598    tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES, tr_sessionIsIncompleteFileNamingEnabled( s ) );
1599    tr_bencDictAddInt ( d, "rpc-version", RPC_VERSION );
1600    tr_bencDictAddInt ( d, "rpc-version-minimum", RPC_VERSION_MIN );
1601    tr_bencDictAddReal( d, "seedRatioLimit", tr_sessionGetRatioLimit( s ) );
1602    tr_bencDictAddBool( d, "seedRatioLimited", tr_sessionIsRatioLimited( s ) );
1603    tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT, tr_sessionGetIdleLimit( s ) );
1604    tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, tr_sessionIsIdleLimited( s ) );
1605    tr_bencDictAddBool( d, TR_PREFS_KEY_START, !tr_sessionGetPaused( s ) );
1606    tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL, tr_sessionGetDeleteSource( s ) );
1607    tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_UP ) );
1608    tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_UP ) );
1609    tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps, tr_sessionGetSpeedLimit_KBps( s, TR_DOWN ) );
1610    tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED, tr_sessionIsSpeedLimited( s, TR_DOWN ) );
1611    tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, tr_sessionGetTorrentDoneScript( s ) );
1612    tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, tr_sessionIsTorrentDoneScriptEnabled( s ) );
1613    tr_formatter_get_units( tr_bencDictAddDict( d, "units", 0 ) );
1614    tr_bencDictAddStr ( d, "version", LONG_VERSION_STRING );
1615    switch( tr_sessionGetEncryption( s ) ) {
1616        case TR_CLEAR_PREFERRED: str = "tolerated"; break;
1617        case TR_ENCRYPTION_REQUIRED: str = "required"; break;
1618        default: str = "preferred"; break;
1619    }
1620    tr_bencDictAddStr( d, TR_PREFS_KEY_ENCRYPTION, str );
1621   
1622    return NULL;
1623}
1624
1625/***
1626****
1627***/
1628
1629static const char*
1630sessionClose( tr_session               * session,
1631              tr_benc                  * args_in UNUSED,
1632              tr_benc                  * args_out UNUSED,
1633              struct tr_rpc_idle_data  * idle_data UNUSED )
1634{
1635    notify( session, TR_RPC_SESSION_CLOSE, NULL );
1636    return NULL;
1637}
1638
1639/***
1640****
1641***/
1642
1643typedef const char* ( *handler )( tr_session*, tr_benc*, tr_benc*, struct tr_rpc_idle_data * );
1644
1645static struct method
1646{
1647    const char *  name;
1648    tr_bool       immediate;
1649    handler       func;
1650}
1651methods[] =
1652{
1653    { "port-test",             FALSE, portTest            },
1654    { "blocklist-update",      FALSE, blocklistUpdate     },
1655    { "session-close",         TRUE,  sessionClose        },
1656    { "session-get",           TRUE,  sessionGet          },
1657    { "session-set",           TRUE,  sessionSet          },
1658    { "session-stats",         TRUE,  sessionStats        },
1659    { "torrent-add",           FALSE, torrentAdd          },
1660    { "torrent-get",           TRUE,  torrentGet          },
1661    { "torrent-remove",        TRUE,  torrentRemove       },
1662    { "torrent-set",           TRUE,  torrentSet          },
1663    { "torrent-set-location",  TRUE,  torrentSetLocation  },
1664    { "torrent-start",         TRUE,  torrentStart        },
1665    { "torrent-stop",          TRUE,  torrentStop         },
1666    { "torrent-verify",        TRUE,  torrentVerify       },
1667    { "torrent-reannounce",    TRUE,  torrentReannounce   }
1668};
1669
1670static void
1671noop_response_callback( tr_session       * session UNUSED,
1672                        struct evbuffer  * response UNUSED,
1673                        void             * user_data UNUSED )
1674{
1675}
1676
1677static void
1678request_exec( tr_session             * session,
1679              tr_benc                * request,
1680              tr_rpc_response_func     callback,
1681              void                   * callback_user_data )
1682{
1683    int i;
1684    const char * str;
1685    tr_benc * args_in = tr_bencDictFind( request, "arguments" );
1686    const char * result = NULL;
1687
1688    if( callback == NULL )
1689        callback = noop_response_callback;
1690
1691    /* parse the request */
1692    if( !tr_bencDictFindStr( request, "method", &str ) )
1693        result = "no method name";
1694    else {
1695        const int n = TR_N_ELEMENTS( methods );
1696        for( i = 0; i < n; ++i )
1697            if( !strcmp( str, methods[i].name ) )
1698                break;
1699        if( i ==n )
1700            result = "method name not recognized";
1701    }
1702
1703    /* if we couldn't figure out which method to use, return an error */
1704    if( result != NULL )
1705    {
1706        int64_t tag;
1707        tr_benc response;
1708        struct evbuffer * buf = evbuffer_new( );
1709
1710        tr_bencInitDict( &response, 3 );
1711        tr_bencDictAddDict( &response, "arguments", 0 );
1712        tr_bencDictAddStr( &response, "result", result );
1713        if( tr_bencDictFindInt( request, "tag", &tag ) )
1714            tr_bencDictAddInt( &response, "tag", tag );
1715        tr_bencToBuf( &response, TR_FMT_JSON_LEAN, buf );
1716        (*callback)( session, buf, callback_user_data );
1717
1718        evbuffer_free( buf );
1719        tr_bencFree( &response );
1720    }
1721    else if( methods[i].immediate )
1722    {
1723        int64_t tag;
1724        tr_benc response;
1725        tr_benc * args_out;
1726        struct evbuffer * buf = evbuffer_new( );
1727
1728        tr_bencInitDict( &response, 3 );
1729        args_out = tr_bencDictAddDict( &response, "arguments", 0 );
1730        result = (*methods[i].func)( session, args_in, args_out, NULL );
1731        if( result == NULL )
1732            result = "success";
1733        tr_bencDictAddStr( &response, "result", result );
1734        if( tr_bencDictFindInt( request, "tag", &tag ) )
1735            tr_bencDictAddInt( &response, "tag", tag );
1736        tr_bencToBuf( &response, TR_FMT_JSON_LEAN, buf );
1737        (*callback)( session, buf, callback_user_data );
1738
1739        evbuffer_free( buf );
1740        tr_bencFree( &response );
1741    }
1742    else
1743    {
1744        int64_t tag;
1745        struct tr_rpc_idle_data * data = tr_new0( struct tr_rpc_idle_data, 1 );
1746        data->session = session;
1747        data->response = tr_new0( tr_benc, 1 );
1748        tr_bencInitDict( data->response, 3 );
1749        if( tr_bencDictFindInt( request, "tag", &tag ) )
1750            tr_bencDictAddInt( data->response, "tag", tag );
1751        data->args_out = tr_bencDictAddDict( data->response, "arguments", 0 );
1752        data->callback = callback;
1753        data->callback_user_data = callback_user_data;
1754        (*methods[i].func)( session, args_in, data->args_out, data );
1755    }
1756}
1757
1758void
1759tr_rpc_request_exec_json( tr_session            * session,
1760                          const void            * request_json,
1761                          int                     request_len,
1762                          tr_rpc_response_func    callback,
1763                          void                  * callback_user_data )
1764{
1765    tr_benc top;
1766    int have_content;
1767
1768    if( request_len < 0 )
1769        request_len = strlen( request_json );
1770
1771    have_content = !tr_jsonParse( "rpc", request_json, request_len, &top, NULL );
1772    request_exec( session, have_content ? &top : NULL, callback, callback_user_data );
1773
1774    if( have_content )
1775        tr_bencFree( &top );
1776}
1777
1778/**
1779 * Munge the URI into a usable form.
1780 *
1781 * We have very loose typing on this to make the URIs as simple as possible:
1782 * - anything not a 'tag' or 'method' is automatically in 'arguments'
1783 * - values that are all-digits are numbers
1784 * - values that are all-digits or commas are number lists
1785 * - all other values are strings
1786 */
1787void
1788tr_rpc_parse_list_str( tr_benc     * setme,
1789                       const char  * str,
1790                       int           len )
1791
1792{
1793    int valueCount;
1794    int * values = tr_parseNumberRange( str, len, &valueCount );
1795
1796    if( valueCount == 0 )
1797        tr_bencInitStr( setme, str, len );
1798    else if( valueCount == 1 )
1799        tr_bencInitInt( setme, values[0] );
1800    else {
1801        int i;
1802        tr_bencInitList( setme, valueCount );
1803        for( i=0; i<valueCount; ++i )
1804            tr_bencListAddInt( setme, values[i] );
1805    }
1806
1807    tr_free( values );
1808}
1809
1810void
1811tr_rpc_request_exec_uri( tr_session           * session,
1812                         const void           * request_uri,
1813                         int                    request_len,
1814                         tr_rpc_response_func   callback,
1815                         void                 * callback_user_data )
1816{
1817    tr_benc      top, * args;
1818    char *       request = tr_strndup( request_uri, request_len );
1819    const char * pch;
1820
1821    tr_bencInitDict( &top, 3 );
1822    args = tr_bencDictAddDict( &top, "arguments", 0 );
1823
1824    pch = strchr( request, '?' );
1825    if( !pch ) pch = request;
1826    while( pch )
1827    {
1828        const char * delim = strchr( pch, '=' );
1829        const char * next = strchr( pch, '&' );
1830        if( delim )
1831        {
1832            char *    key = tr_strndup( pch, delim - pch );
1833            int       isArg = strcmp( key, "method" ) && strcmp( key, "tag" );
1834            tr_benc * parent = isArg ? args : &top;
1835            tr_rpc_parse_list_str( tr_bencDictAdd( parent, key ),
1836                                  delim + 1,
1837                                  next ? (size_t)(
1838                                       next -
1839                                      ( delim + 1 ) ) : strlen( delim + 1 ) );
1840            tr_free( key );
1841        }
1842        pch = next ? next + 1 : NULL;
1843    }
1844
1845    request_exec( session, &top, callback, callback_user_data );
1846
1847    /* cleanup */
1848    tr_bencFree( &top );
1849    tr_free( request );
1850}
Note: See TracBrowser for help on using the repository browser.