source: trunk/libtransmission/rpcimpl.c @ 13631

Last change on this file since 13631 was 13631, checked in by jordan, 9 years ago

(trunk, libT) #5165: fix r13625 oops

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