source: trunk/libtransmission/rpcimpl.c @ 13667

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

refactor libtransmission's tr_benc class as tr_variant.

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