source: trunk/libtransmission/rpcimpl.c @ 13787

Last change on this file since 13787 was 13787, checked in by jordan, 8 years ago

(libT) #5220 'add etaIdle to torrent-get in RPC' -- done.

  • Property svn:keywords set to Date Rev Author Id
File size: 71.0 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 13787 2013-01-13 20:39:56Z 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     15
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, TR_KEY_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, TR_KEY_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, TR_KEY_ids, &id)
147           || tr_variantDictFindInt (args, TR_KEY_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, TR_KEY_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, TR_KEY_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, TR_KEY_bytesCompleted, files[i].bytesCompleted);
420        tr_variantDictAddInt (d, TR_KEY_priority, file->priority);
421        tr_variantDictAddBool (d, TR_KEY_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, TR_KEY_bytesCompleted, files[i].bytesCompleted);
441        tr_variantDictAddInt (d, TR_KEY_length, file->length);
442        tr_variantDictAddStr (d, TR_KEY_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  unsigned 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  unsigned 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, TR_KEY_announce, t->announce);
469      tr_variantDictAddInt (d, TR_KEY_id, t->id);
470      tr_variantDictAddStr (d, TR_KEY_scrape, t->scrape);
471      tr_variantDictAddInt (d, TR_KEY_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, TR_KEY_announce, s->announce);
485        tr_variantDictAddInt  (d, TR_KEY_announceState, s->announceState);
486        tr_variantDictAddInt  (d, TR_KEY_downloadCount, s->downloadCount);
487        tr_variantDictAddBool (d, TR_KEY_hasAnnounced, s->hasAnnounced);
488        tr_variantDictAddBool (d, TR_KEY_hasScraped, s->hasScraped);
489        tr_variantDictAddStr  (d, TR_KEY_host, s->host);
490        tr_variantDictAddInt  (d, TR_KEY_id, s->id);
491        tr_variantDictAddBool (d, TR_KEY_isBackup, s->isBackup);
492        tr_variantDictAddInt  (d, TR_KEY_lastAnnouncePeerCount, s->lastAnnouncePeerCount);
493        tr_variantDictAddStr  (d, TR_KEY_lastAnnounceResult, s->lastAnnounceResult);
494        tr_variantDictAddInt  (d, TR_KEY_lastAnnounceStartTime, s->lastAnnounceStartTime);
495        tr_variantDictAddBool (d, TR_KEY_lastAnnounceSucceeded, s->lastAnnounceSucceeded);
496        tr_variantDictAddInt  (d, TR_KEY_lastAnnounceTime, s->lastAnnounceTime);
497        tr_variantDictAddBool (d, TR_KEY_lastAnnounceTimedOut, s->lastAnnounceTimedOut);
498        tr_variantDictAddStr  (d, TR_KEY_lastScrapeResult, s->lastScrapeResult);
499        tr_variantDictAddInt  (d, TR_KEY_lastScrapeStartTime, s->lastScrapeStartTime);
500        tr_variantDictAddBool (d, TR_KEY_lastScrapeSucceeded, s->lastScrapeSucceeded);
501        tr_variantDictAddInt  (d, TR_KEY_lastScrapeTime, s->lastScrapeTime);
502        tr_variantDictAddInt  (d, TR_KEY_lastScrapeTimedOut, s->lastScrapeTimedOut);
503        tr_variantDictAddInt  (d, TR_KEY_leecherCount, s->leecherCount);
504        tr_variantDictAddInt  (d, TR_KEY_nextAnnounceTime, s->nextAnnounceTime);
505        tr_variantDictAddInt  (d, TR_KEY_nextScrapeTime, s->nextScrapeTime);
506        tr_variantDictAddStr  (d, TR_KEY_scrape, s->scrape);
507        tr_variantDictAddInt  (d, TR_KEY_scrapeState, s->scrapeState);
508        tr_variantDictAddInt  (d, TR_KEY_seederCount, s->seederCount);
509        tr_variantDictAddInt  (d, TR_KEY_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, TR_KEY_address, peer->addr);
527        tr_variantDictAddStr  (d, TR_KEY_clientName, peer->client);
528        tr_variantDictAddBool (d, TR_KEY_clientIsChoked, peer->clientIsChoked);
529        tr_variantDictAddBool (d, TR_KEY_clientIsInterested, peer->clientIsInterested);
530        tr_variantDictAddStr  (d, TR_KEY_flagStr, peer->flagStr);
531        tr_variantDictAddBool (d, TR_KEY_isDownloadingFrom, peer->isDownloadingFrom);
532        tr_variantDictAddBool (d, TR_KEY_isEncrypted, peer->isEncrypted);
533        tr_variantDictAddBool (d, TR_KEY_isIncoming, peer->isIncoming);
534        tr_variantDictAddBool (d, TR_KEY_isUploadingTo, peer->isUploadingTo);
535        tr_variantDictAddBool (d, TR_KEY_isUTP, peer->isUTP);
536        tr_variantDictAddBool (d, TR_KEY_peerIsChoked, peer->peerIsChoked);
537        tr_variantDictAddBool (d, TR_KEY_peerIsInterested, peer->peerIsInterested);
538        tr_variantDictAddInt  (d, TR_KEY_port, peer->port);
539        tr_variantDictAddReal (d, TR_KEY_progress, peer->progress);
540        tr_variantDictAddInt  (d, TR_KEY_rateToClient, toSpeedBytes (peer->rateToClient_KBps));
541        tr_variantDictAddInt  (d, TR_KEY_rateToPeer, toSpeedBytes (peer->rateToPeer_KBps));
542    }
543
544    tr_torrentPeersFree (peers, peerCount);
545}
546
547static void
548addField (tr_torrent       * const tor,
549          const tr_info    * const inf,
550          const tr_stat    * const st,
551          tr_variant       * const d,
552          const tr_quark           key)
553{
554  char * str;
555
556  switch (key)
557    {
558      case TR_KEY_activityDate:
559        tr_variantDictAddInt (d, key, st->activityDate);
560        break;
561
562      case TR_KEY_addedDate:
563        tr_variantDictAddInt (d, key, st->addedDate);
564        break;
565
566      case TR_KEY_bandwidthPriority:
567        tr_variantDictAddInt (d, key, tr_torrentGetPriority (tor));
568        break;
569
570      case TR_KEY_comment:
571        tr_variantDictAddStr (d, key, inf->comment ? inf->comment : "");
572        break;
573
574      case TR_KEY_corruptEver:
575        tr_variantDictAddInt (d, key, st->corruptEver);
576        break;
577
578      case TR_KEY_creator:
579        tr_variantDictAddStr (d, key, inf->creator ? inf->creator : "");
580        break;
581
582      case TR_KEY_dateCreated:
583        tr_variantDictAddInt (d, key, inf->dateCreated);
584        break;
585
586      case TR_KEY_desiredAvailable:
587        tr_variantDictAddInt (d, key, st->desiredAvailable);
588        break;
589
590      case TR_KEY_doneDate:
591        tr_variantDictAddInt (d, key, st->doneDate);
592        break;
593
594      case TR_KEY_downloadDir:
595        tr_variantDictAddStr (d, key, tr_torrentGetDownloadDir (tor));
596        break;
597
598      case TR_KEY_downloadedEver:
599        tr_variantDictAddInt (d, key, st->downloadedEver);
600        break;
601
602      case TR_KEY_downloadLimit:
603        tr_variantDictAddInt (d, key, tr_torrentGetSpeedLimit_KBps (tor, TR_DOWN));
604        break;
605
606      case TR_KEY_downloadLimited:
607        tr_variantDictAddBool (d, key, tr_torrentUsesSpeedLimit (tor, TR_DOWN));
608        break;
609
610      case TR_KEY_error:
611        tr_variantDictAddInt (d, key, st->error);
612        break;
613
614      case TR_KEY_errorString:
615        tr_variantDictAddStr (d, key, st->errorString);
616        break;
617
618      case TR_KEY_eta:
619        tr_variantDictAddInt (d, key, st->eta);
620        break;
621
622      case TR_KEY_files:
623        addFiles (tor, tr_variantDictAddList (d, key, inf->fileCount));
624        break;
625
626      case TR_KEY_fileStats:
627        addFileStats (tor, tr_variantDictAddList (d, key, inf->fileCount));
628        break;
629
630      case TR_KEY_hashString:
631        tr_variantDictAddStr (d, key, tor->info.hashString);
632        break;
633
634      case TR_KEY_haveUnchecked:
635        tr_variantDictAddInt (d, key, st->haveUnchecked);
636        break;
637
638      case TR_KEY_haveValid:
639        tr_variantDictAddInt (d, key, st->haveValid);
640        break;
641
642      case TR_KEY_honorsSessionLimits:
643        tr_variantDictAddBool (d, key, tr_torrentUsesSessionLimits (tor));
644        break;
645
646      case TR_KEY_id:
647        tr_variantDictAddInt (d, key, st->id);
648        break;
649
650      case TR_KEY_isFinished:
651        tr_variantDictAddBool (d, key, st->finished);
652        break;
653
654      case TR_KEY_isPrivate:
655        tr_variantDictAddBool (d, key, tr_torrentIsPrivate (tor));
656        break;
657
658      case TR_KEY_isStalled:
659        tr_variantDictAddBool (d, key, st->isStalled);
660        break;
661
662      case TR_KEY_leftUntilDone:
663        tr_variantDictAddInt (d, key, st->leftUntilDone);
664        break;
665
666      case TR_KEY_manualAnnounceTime:
667        tr_variantDictAddInt (d, key, st->manualAnnounceTime);
668        break;
669
670      case TR_KEY_maxConnectedPeers:
671        tr_variantDictAddInt (d, key,  tr_torrentGetPeerLimit (tor));
672        break;
673
674      case TR_KEY_magnetLink:
675        str = tr_torrentGetMagnetLink (tor);
676        tr_variantDictAddStr (d, key, str);
677        tr_free (str);
678        break;
679
680      case TR_KEY_metadataPercentComplete:
681        tr_variantDictAddReal (d, key, st->metadataPercentComplete);
682        break;
683
684      case TR_KEY_name:
685        tr_variantDictAddStr (d, key, tr_torrentName (tor));
686        break;
687
688      case TR_KEY_percentDone:
689        tr_variantDictAddReal (d, key, st->percentDone);
690        break;
691
692      case TR_KEY_peer_limit:
693        tr_variantDictAddInt (d, key, tr_torrentGetPeerLimit (tor));
694        break;
695
696      case TR_KEY_peers:
697        addPeers (tor, tr_variantDictAdd (d, key));
698        break;
699
700      case TR_KEY_peersConnected:
701        tr_variantDictAddInt (d, key, st->peersConnected);
702        break;
703
704      case TR_KEY_peersFrom:
705        {
706          tr_variant * tmp = tr_variantDictAddDict (d, key, 7);
707          const int * f = st->peersFrom;
708          tr_variantDictAddInt (tmp, TR_KEY_fromCache,    f[TR_PEER_FROM_RESUME]);
709          tr_variantDictAddInt (tmp, TR_KEY_fromDht,      f[TR_PEER_FROM_DHT]);
710          tr_variantDictAddInt (tmp, TR_KEY_fromIncoming, f[TR_PEER_FROM_INCOMING]);
711          tr_variantDictAddInt (tmp, TR_KEY_fromLpd,      f[TR_PEER_FROM_LPD]);
712          tr_variantDictAddInt (tmp, TR_KEY_fromLtep,     f[TR_PEER_FROM_LTEP]);
713          tr_variantDictAddInt (tmp, TR_KEY_fromPex,      f[TR_PEER_FROM_PEX]);
714          tr_variantDictAddInt (tmp, TR_KEY_fromTracker,  f[TR_PEER_FROM_TRACKER]);
715          break;
716        }
717
718      case TR_KEY_peersGettingFromUs:
719        tr_variantDictAddInt (d, key, st->peersGettingFromUs);
720        break;
721
722      case TR_KEY_peersSendingToUs:
723        tr_variantDictAddInt (d, key, st->peersSendingToUs);
724        break;
725
726      case TR_KEY_pieces:
727        if (tr_torrentHasMetadata (tor))
728          {
729            size_t byte_count = 0;
730            void * bytes = tr_cpCreatePieceBitfield (&tor->completion, &byte_count);
731            char * str = tr_base64_encode (bytes, byte_count, NULL);
732            tr_variantDictAddStr (d, key, str!=NULL ? str : "");
733            tr_free (str);
734            tr_free (bytes);
735          }
736        else
737          {
738            tr_variantDictAddStr (d, key, "");
739          }
740        break;
741
742      case TR_KEY_pieceCount:
743        tr_variantDictAddInt (d, key, inf->pieceCount);
744        break;
745
746      case TR_KEY_pieceSize:
747        tr_variantDictAddInt (d, key, inf->pieceSize);
748        break;
749
750      case TR_KEY_priorities:
751        {
752          tr_file_index_t i;
753          tr_variant * p = tr_variantDictAddList (d, key, inf->fileCount);
754          for (i=0; i<inf->fileCount; ++i)
755            tr_variantListAddInt (p, inf->files[i].priority);
756          break;
757        }
758
759      case TR_KEY_queuePosition:
760        tr_variantDictAddInt (d, key, st->queuePosition);
761        break;
762
763      case TR_KEY_etaIdle:
764        tr_variantDictAddInt (d, key, st->etaIdle);
765        break;
766
767      case TR_KEY_rateDownload:
768        tr_variantDictAddInt (d, key, toSpeedBytes (st->pieceDownloadSpeed_KBps));
769        break;
770
771      case TR_KEY_rateUpload:
772        tr_variantDictAddInt (d, key, toSpeedBytes (st->pieceUploadSpeed_KBps));
773        break;
774
775      case TR_KEY_recheckProgress:
776        tr_variantDictAddReal (d, key, st->recheckProgress);
777        break;
778
779      case TR_KEY_seedIdleLimit:
780        tr_variantDictAddInt (d, key, tr_torrentGetIdleLimit (tor));
781        break;
782
783      case TR_KEY_seedIdleMode:
784        tr_variantDictAddInt (d, key, tr_torrentGetIdleMode (tor));
785        break;
786
787      case TR_KEY_seedRatioLimit:
788        tr_variantDictAddReal (d, key, tr_torrentGetRatioLimit (tor));
789        break;
790
791      case TR_KEY_seedRatioMode:
792        tr_variantDictAddInt (d, key, tr_torrentGetRatioMode (tor));
793        break;
794
795      case TR_KEY_sizeWhenDone:
796        tr_variantDictAddInt (d, key, st->sizeWhenDone);
797        break;
798
799      case TR_KEY_startDate:
800        tr_variantDictAddInt (d, key, st->startDate);
801        break;
802
803      case TR_KEY_status:
804        tr_variantDictAddInt (d, key, st->activity);
805        break;
806
807      case TR_KEY_secondsDownloading:
808        tr_variantDictAddInt (d, key, st->secondsDownloading);
809        break;
810
811      case TR_KEY_secondsSeeding:
812        tr_variantDictAddInt (d, key, st->secondsSeeding);
813        break;
814
815      case TR_KEY_trackers:
816        addTrackers (inf, tr_variantDictAddList (d, key, inf->trackerCount));
817        break;
818
819      case TR_KEY_trackerStats:
820        {
821          int n;
822          tr_tracker_stat * s = tr_torrentTrackers (tor, &n);
823          addTrackerStats (s, n, tr_variantDictAddList (d, key, n));
824          tr_torrentTrackersFree (s, n);
825          break;
826        }
827
828      case TR_KEY_torrentFile:
829        tr_variantDictAddStr (d, key, inf->torrent);
830        break;
831
832      case TR_KEY_totalSize:
833        tr_variantDictAddInt (d, key, inf->totalSize);
834        break;
835
836      case TR_KEY_uploadedEver:
837        tr_variantDictAddInt (d, key, st->uploadedEver);
838        break;
839
840      case TR_KEY_uploadLimit:
841        tr_variantDictAddInt (d, key, tr_torrentGetSpeedLimit_KBps (tor, TR_UP));
842        break;
843
844      case TR_KEY_uploadLimited:
845        tr_variantDictAddBool (d, key, tr_torrentUsesSpeedLimit (tor, TR_UP));
846        break;
847
848      case TR_KEY_uploadRatio:
849        tr_variantDictAddReal (d, key, st->ratio);
850        break;
851
852      case TR_KEY_wanted:
853        {
854          tr_file_index_t i;
855          tr_variant * w = tr_variantDictAddList (d, key, inf->fileCount);
856          for (i=0; i<inf->fileCount; ++i)
857            tr_variantListAddInt (w, inf->files[i].dnd ? 0 : 1);
858          break;
859        }
860
861      case TR_KEY_webseeds:
862        addWebseeds (inf, tr_variantDictAddList (d, key, inf->webseedCount));
863        break;
864
865      case TR_KEY_webseedsSendingToUs:
866        tr_variantDictAddInt (d, key, st->webseedsSendingToUs);
867        break;
868
869      default:
870        break;
871    }
872}
873
874static void
875addInfo (tr_torrent * tor, tr_variant * d, tr_variant * fields)
876{
877  const int n = tr_variantListSize (fields);
878
879  tr_variantInitDict (d, n);
880
881  if (n > 0)
882    {
883      int i;
884      const tr_info const * inf = tr_torrentInfo (tor);
885      const tr_stat const * st = tr_torrentStat ((tr_torrent*)tor);
886
887      for (i=0; i<n; ++i)
888        {
889          size_t len;
890          const char * str;
891          if (tr_variantGetStr (tr_variantListChild (fields, i), &str, &len))
892            addField (tor, inf, st, d, tr_quark_new (str, len));
893        }
894    }
895}
896
897static const char*
898torrentGet (tr_session               * session,
899            tr_variant                  * args_in,
900            tr_variant                  * args_out,
901            struct tr_rpc_idle_data  * idle_data UNUSED)
902{
903    int           i, torrentCount;
904    tr_torrent ** torrents = getTorrents (session, args_in, &torrentCount);
905    tr_variant *     list = tr_variantDictAddList (args_out, TR_KEY_torrents, torrentCount);
906    tr_variant *     fields;
907    const char *  msg = NULL;
908    const char *  strVal;
909
910    assert (idle_data == NULL);
911
912    if (tr_variantDictFindStr (args_in, TR_KEY_ids, &strVal, NULL) && !strcmp (strVal, "recently-active")) {
913        int n = 0;
914        tr_variant * d;
915        const time_t now = tr_time ();
916        const int interval = RECENTLY_ACTIVE_SECONDS;
917        tr_variant * removed_out = tr_variantDictAddList (args_out, TR_KEY_removed, 0);
918        while ((d = tr_variantListChild (&session->removedTorrents, n++))) {
919            int64_t intVal;
920            if (tr_variantDictFindInt (d, TR_KEY_date, &intVal) && (intVal >= now - interval)) {
921                tr_variantDictFindInt (d, TR_KEY_id, &intVal);
922                tr_variantListAddInt (removed_out, intVal);
923            }
924        }
925    }
926
927    if (!tr_variantDictFindList (args_in, TR_KEY_fields, &fields))
928        msg = "no fields specified";
929    else for (i = 0; i < torrentCount; ++i)
930        addInfo (torrents[i], tr_variantListAdd (list), fields);
931
932    tr_free (torrents);
933    return msg;
934}
935
936/***
937****
938***/
939
940static const char*
941setFilePriorities (tr_torrent * tor,
942                   int          priority,
943                   tr_variant *    list)
944{
945    int i;
946    int64_t tmp;
947    int fileCount = 0;
948    const int n = tr_variantListSize (list);
949    const char * errmsg = NULL;
950    tr_file_index_t * files = tr_new0 (tr_file_index_t, tor->info.fileCount);
951
952    if (n)
953    {
954        for (i = 0; i < n; ++i) {
955            if (tr_variantGetInt (tr_variantListChild (list, i), &tmp)) {
956                if (0 <= tmp && tmp < tor->info.fileCount) {
957                    files[fileCount++] = tmp;
958                } else {
959                    errmsg = "file index out of range";
960                }
961            }
962        }
963    }
964    else /* if empty set, apply to all */
965    {
966        tr_file_index_t t;
967        for (t = 0; t < tor->info.fileCount; ++t)
968            files[fileCount++] = t;
969    }
970
971    if (fileCount)
972        tr_torrentSetFilePriorities (tor, files, fileCount, priority);
973
974    tr_free (files);
975    return errmsg;
976}
977
978static const char*
979setFileDLs (tr_torrent * tor,
980            int          do_download,
981            tr_variant *    list)
982{
983    int i;
984    int64_t tmp;
985    int fileCount = 0;
986    const int n = tr_variantListSize (list);
987    const char * errmsg = NULL;
988    tr_file_index_t * files = tr_new0 (tr_file_index_t, tor->info.fileCount);
989
990    if (n) /* if argument list, process them */
991    {
992        for (i = 0; i < n; ++i) {
993            if (tr_variantGetInt (tr_variantListChild (list, i), &tmp)) {
994                if (0 <= tmp && tmp < tor->info.fileCount) {
995                    files[fileCount++] = tmp;
996                } else {
997                    errmsg = "file index out of range";
998                }
999            }
1000        }
1001    }
1002    else /* if empty set, apply to all */
1003    {
1004        tr_file_index_t t;
1005        for (t = 0; t < tor->info.fileCount; ++t)
1006            files[fileCount++] = t;
1007    }
1008
1009    if (fileCount)
1010        tr_torrentSetFileDLs (tor, files, fileCount, do_download);
1011
1012    tr_free (files);
1013    return errmsg;
1014}
1015
1016static bool
1017findAnnounceUrl (const tr_tracker_info * t, int n, const char * url, int * pos)
1018{
1019    int i;
1020    bool found = false;
1021
1022    for (i=0; i<n; ++i)
1023    {
1024        if (!strcmp (t[i].announce, url))
1025        {
1026            found = true;
1027            if (pos) *pos = i;
1028            break;
1029        }
1030    }
1031
1032    return found;
1033}
1034
1035static int
1036copyTrackers (tr_tracker_info * tgt, const tr_tracker_info * src, int n)
1037{
1038    int i;
1039    int maxTier = -1;
1040
1041    for (i=0; i<n; ++i)
1042    {
1043        tgt[i].tier = src[i].tier;
1044        tgt[i].announce = tr_strdup (src[i].announce);
1045        maxTier = MAX (maxTier, src[i].tier);
1046    }
1047
1048    return maxTier;
1049}
1050
1051static void
1052freeTrackers (tr_tracker_info * trackers, int n)
1053{
1054    int i;
1055
1056    for (i=0; i<n; ++i)
1057        tr_free (trackers[i].announce);
1058
1059    tr_free (trackers);
1060}
1061
1062static const char*
1063addTrackerUrls (tr_torrent * tor, tr_variant * urls)
1064{
1065    int i;
1066    int n;
1067    int tier;
1068    tr_variant * val;
1069    tr_tracker_info * trackers;
1070    bool changed = false;
1071    const tr_info * inf = tr_torrentInfo (tor);
1072    const char * errmsg = NULL;
1073
1074    /* make a working copy of the existing announce list */
1075    n = inf->trackerCount;
1076    trackers = tr_new0 (tr_tracker_info, n + tr_variantListSize (urls));
1077    tier = copyTrackers (trackers, inf->trackers, n);
1078
1079    /* and add the new ones */
1080    i = 0;
1081    while ((val = tr_variantListChild (urls, i++)))
1082    {
1083        const char * announce = NULL;
1084
1085        if ( tr_variantGetStr (val, &announce, NULL)
1086            && tr_urlIsValidTracker (announce)
1087            && !findAnnounceUrl (trackers, n, announce, NULL))
1088        {
1089            trackers[n].tier = ++tier; /* add a new tier */
1090            trackers[n].announce = tr_strdup (announce);
1091            ++n;
1092            changed = true;
1093        }
1094    }
1095
1096    if (!changed)
1097        errmsg = "invalid argument";
1098    else if (!tr_torrentSetAnnounceList (tor, trackers, n))
1099        errmsg = "error setting announce list";
1100
1101    freeTrackers (trackers, n);
1102    return errmsg;
1103}
1104
1105static const char*
1106replaceTrackers (tr_torrent * tor, tr_variant * urls)
1107{
1108    int i;
1109    tr_variant * pair[2];
1110    tr_tracker_info * trackers;
1111    bool changed = false;
1112    const tr_info * inf = tr_torrentInfo (tor);
1113    const int n = inf->trackerCount;
1114    const char * errmsg = NULL;
1115
1116    /* make a working copy of the existing announce list */
1117    trackers = tr_new0 (tr_tracker_info, n);
1118    copyTrackers (trackers, inf->trackers, n);
1119
1120    /* make the substitutions... */
1121    i = 0;
1122    while (((pair[0] = tr_variantListChild (urls,i))) &&
1123        ((pair[1] = tr_variantListChild (urls,i+1))))
1124    {
1125        size_t len;
1126        int64_t pos;
1127        const char * newval;
1128
1129        if ( tr_variantGetInt (pair[0], &pos)
1130            && tr_variantGetStr (pair[1], &newval, &len)
1131            && tr_urlIsValidTracker (newval)
1132            && pos < n
1133            && pos >= 0)
1134        {
1135            tr_free (trackers[pos].announce);
1136            trackers[pos].announce = tr_strndup (newval, len);
1137            changed = true;
1138        }
1139
1140        i += 2;
1141    }
1142
1143    if (!changed)
1144        errmsg = "invalid argument";
1145    else if (!tr_torrentSetAnnounceList (tor, trackers, n))
1146        errmsg = "error setting announce list";
1147
1148    freeTrackers (trackers, n);
1149    return errmsg;
1150}
1151
1152static const char*
1153removeTrackers (tr_torrent * tor, tr_variant * ids)
1154{
1155    int i;
1156    int n;
1157    int t = 0;
1158    int dup = -1;
1159    int * tids;
1160    tr_variant * val;
1161    tr_tracker_info * trackers;
1162    bool changed = false;
1163    const tr_info * inf = tr_torrentInfo (tor);
1164    const char * errmsg = NULL;
1165
1166    /* make a working copy of the existing announce list */
1167    n = inf->trackerCount;
1168    tids = tr_new0 (int, n);
1169    trackers = tr_new0 (tr_tracker_info, n);
1170    copyTrackers (trackers, inf->trackers, n);
1171
1172    /* remove the ones specified in the urls list */
1173    i = 0;
1174    while ((val = tr_variantListChild (ids, i++)))
1175    {
1176        int64_t pos;
1177
1178        if ( tr_variantGetInt (val, &pos)
1179            && pos < n
1180            && pos >= 0)
1181            tids[t++] = pos;
1182    }
1183
1184    /* sort trackerIds and remove from largest to smallest so there is no need to recacluate array indicies */
1185    qsort (tids, t, sizeof (int), compareInt);
1186    while (t--)
1187    {
1188        /* check for duplicates */
1189        if (tids[t] == dup)
1190            continue;
1191        tr_removeElementFromArray (trackers, tids[t], sizeof (tr_tracker_info), n--);
1192        dup = tids[t];
1193        changed = true;
1194    }
1195
1196    if (!changed)
1197        errmsg = "invalid argument";
1198    else if (!tr_torrentSetAnnounceList (tor, trackers, n))
1199        errmsg = "error setting announce list";
1200
1201    freeTrackers (trackers, n);
1202    tr_free (tids);
1203    return errmsg;
1204}
1205
1206static const char*
1207torrentSet (tr_session               * session,
1208            tr_variant                  * args_in,
1209            tr_variant                  * args_out UNUSED,
1210            struct tr_rpc_idle_data  * idle_data UNUSED)
1211{
1212    const char * errmsg = NULL;
1213    int i, torrentCount;
1214    tr_torrent ** torrents = getTorrents (session, args_in, &torrentCount);
1215
1216    assert (idle_data == NULL);
1217
1218    for (i = 0; i < torrentCount; ++i)
1219    {
1220        int64_t      tmp;
1221        double       d;
1222        tr_variant *    files;
1223        tr_variant *    trackers;
1224        bool         boolVal;
1225        tr_torrent * tor = torrents[i];
1226
1227        if (tr_variantDictFindInt (args_in, TR_KEY_bandwidthPriority, &tmp))
1228            if (tr_isPriority (tmp))
1229                tr_torrentSetPriority (tor, tmp);
1230        if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_files_unwanted, &files))
1231            errmsg = setFileDLs (tor, false, files);
1232        if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_files_wanted, &files))
1233            errmsg = setFileDLs (tor, true, files);
1234        if (tr_variantDictFindInt (args_in, TR_KEY_peer_limit, &tmp))
1235            tr_torrentSetPeerLimit (tor, tmp);
1236        if (!errmsg &&  tr_variantDictFindList (args_in, TR_KEY_priority_high, &files))
1237            errmsg = setFilePriorities (tor, TR_PRI_HIGH, files);
1238        if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_priority_low, &files))
1239            errmsg = setFilePriorities (tor, TR_PRI_LOW, files);
1240        if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_priority_normal, &files))
1241            errmsg = setFilePriorities (tor, TR_PRI_NORMAL, files);
1242        if (tr_variantDictFindInt (args_in, TR_KEY_downloadLimit, &tmp))
1243            tr_torrentSetSpeedLimit_KBps (tor, TR_DOWN, tmp);
1244        if (tr_variantDictFindBool (args_in, TR_KEY_downloadLimited, &boolVal))
1245            tr_torrentUseSpeedLimit (tor, TR_DOWN, boolVal);
1246        if (tr_variantDictFindBool (args_in, TR_KEY_honorsSessionLimits, &boolVal))
1247            tr_torrentUseSessionLimits (tor, boolVal);
1248        if (tr_variantDictFindInt (args_in, TR_KEY_uploadLimit, &tmp))
1249            tr_torrentSetSpeedLimit_KBps (tor, TR_UP, tmp);
1250        if (tr_variantDictFindBool (args_in, TR_KEY_uploadLimited, &boolVal))
1251            tr_torrentUseSpeedLimit (tor, TR_UP, boolVal);
1252        if (tr_variantDictFindInt (args_in, TR_KEY_seedIdleLimit, &tmp))
1253            tr_torrentSetIdleLimit (tor, tmp);
1254        if (tr_variantDictFindInt (args_in, TR_KEY_seedIdleMode, &tmp))
1255            tr_torrentSetIdleMode (tor, tmp);
1256        if (tr_variantDictFindReal (args_in, TR_KEY_seedRatioLimit, &d))
1257            tr_torrentSetRatioLimit (tor, d);
1258        if (tr_variantDictFindInt (args_in, TR_KEY_seedRatioMode, &tmp))
1259            tr_torrentSetRatioMode (tor, tmp);
1260        if (tr_variantDictFindInt (args_in, TR_KEY_queuePosition, &tmp))
1261            tr_torrentSetQueuePosition (tor, tmp);
1262        if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_trackerAdd, &trackers))
1263            errmsg = addTrackerUrls (tor, trackers);
1264        if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_trackerRemove, &trackers))
1265            errmsg = removeTrackers (tor, trackers);
1266        if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_trackerReplace, &trackers))
1267            errmsg = replaceTrackers (tor, trackers);
1268        notify (session, TR_RPC_TORRENT_CHANGED, tor);
1269    }
1270
1271    tr_free (torrents);
1272    return errmsg;
1273}
1274
1275static const char*
1276torrentSetLocation (tr_session               * session,
1277                    tr_variant               * args_in,
1278                    tr_variant               * args_out UNUSED,
1279                    struct tr_rpc_idle_data  * idle_data UNUSED)
1280{
1281  const char * errmsg = NULL;
1282  const char * location = NULL;
1283
1284  assert (idle_data == NULL);
1285
1286  if (!tr_variantDictFindStr (args_in, TR_KEY_location, &location, NULL))
1287    {
1288      errmsg = "no location";
1289    }
1290  else
1291    {
1292      bool move = false;
1293      int i, torrentCount;
1294      tr_torrent ** torrents = getTorrents (session, args_in, &torrentCount);
1295
1296      tr_variantDictFindBool (args_in, TR_KEY_move, &move);
1297
1298      for (i=0; i<torrentCount; ++i)
1299        {
1300          tr_torrent * tor = torrents[i];
1301          tr_torrentSetLocation (tor, location, move, NULL, NULL);
1302          notify (session, TR_RPC_TORRENT_MOVED, tor);
1303        }
1304
1305      tr_free (torrents);
1306    }
1307
1308  return errmsg;
1309}
1310
1311/***
1312****
1313***/
1314
1315static void
1316portTested (tr_session       * session UNUSED,
1317            bool               did_connect UNUSED,
1318            bool               did_timeout UNUSED,
1319            long               response_code,
1320            const void       * response,
1321            size_t             response_byte_count,
1322            void             * user_data)
1323{
1324    char result[1024];
1325    struct tr_rpc_idle_data * data = user_data;
1326
1327    if (response_code != 200)
1328    {
1329        tr_snprintf (result, sizeof (result), "portTested: http error %ld: %s",
1330                     response_code, tr_webGetResponseStr (response_code));
1331    }
1332    else /* success */
1333    {
1334        const bool isOpen = response_byte_count && * (char*)response == '1';
1335        tr_variantDictAddBool (data->args_out, TR_KEY_port_is_open, isOpen);
1336        tr_snprintf (result, sizeof (result), "success");
1337    }
1338
1339    tr_idle_function_done (data, result);
1340}
1341
1342static const char*
1343portTest (tr_session               * session,
1344          tr_variant                  * args_in UNUSED,
1345          tr_variant                  * args_out UNUSED,
1346          struct tr_rpc_idle_data  * idle_data)
1347{
1348    const int port = tr_sessionGetPeerPort (session);
1349    char * url = tr_strdup_printf ("http://portcheck.transmissionbt.com/%d", port);
1350    tr_webRun (session, url, NULL, NULL, portTested, idle_data);
1351    tr_free (url);
1352    return NULL;
1353}
1354
1355/***
1356****
1357***/
1358
1359static void
1360gotNewBlocklist (tr_session       * session,
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    char result[1024];
1369    struct tr_rpc_idle_data * data = user_data;
1370
1371    *result = '\0';
1372
1373    if (response_code != 200)
1374    {
1375        tr_snprintf (result, sizeof (result), "gotNewBlocklist: http error %ld: %s",
1376                     response_code, tr_webGetResponseStr (response_code));
1377    }
1378    else /* successfully fetched the blocklist... */
1379    {
1380        int fd;
1381        int err;
1382        char * filename;
1383        z_stream stream;
1384        const char * configDir = tr_sessionGetConfigDir (session);
1385        const size_t buflen = 1024 * 128; /* 128 KiB buffer */
1386        uint8_t * buf = tr_valloc (buflen);
1387
1388        /* this is an odd Magic Number required by zlib to enable gz support.
1389           See zlib's inflateInit2 () documentation for a full description */
1390        const int windowBits = 15 + 32;
1391
1392        stream.zalloc = (alloc_func) Z_NULL;
1393        stream.zfree = (free_func) Z_NULL;
1394        stream.opaque = (voidpf) Z_NULL;
1395        stream.next_in = (void*) response;
1396        stream.avail_in = response_byte_count;
1397        inflateInit2 (&stream, windowBits);
1398
1399        filename = tr_buildPath (configDir, "blocklist.tmp", NULL);
1400        fd = tr_open_file_for_writing (filename);
1401        if (fd < 0)
1402            tr_snprintf (result, sizeof (result), _("Couldn't save file \"%1$s\": %2$s"), filename, tr_strerror (errno));
1403
1404        for (;;)
1405        {
1406            stream.next_out = (void*) buf;
1407            stream.avail_out = buflen;
1408            err = inflate (&stream, Z_NO_FLUSH);
1409
1410            if (stream.avail_out < buflen) {
1411                const int e = write (fd, buf, buflen - stream.avail_out);
1412                if (e < 0) {
1413                    tr_snprintf (result, sizeof (result), _("Couldn't save file \"%1$s\": %2$s"), filename, tr_strerror (errno));
1414                    break;
1415                }
1416            }
1417
1418            if (err != Z_OK) {
1419                if ((err != Z_STREAM_END) && (err != Z_DATA_ERROR))
1420                    tr_snprintf (result, sizeof (result), _("Error uncompressing blocklist: %s (%d)"), zError (err), err);
1421                break;
1422            }
1423        }
1424
1425        inflateEnd (&stream);
1426
1427        if (err == Z_DATA_ERROR) /* couldn't inflate it... it's probably already uncompressed */
1428            if (write (fd, response, response_byte_count) < 0)
1429                tr_snprintf (result, sizeof (result), _("Couldn't save file \"%1$s\": %2$s"), filename, tr_strerror (errno));
1430
1431        if (*result)
1432            tr_err ("%s", result);
1433        else {
1434            /* feed it to the session and give the client a response */
1435            const int rule_count = tr_blocklistSetContent (session, filename);
1436            tr_variantDictAddInt (data->args_out, TR_KEY_blocklist_size, rule_count);
1437            tr_snprintf (result, sizeof (result), "success");
1438        }
1439
1440        unlink (filename);
1441        tr_free (filename);
1442        tr_free (buf);
1443    }
1444
1445    tr_idle_function_done (data, result);
1446}
1447
1448static const char*
1449blocklistUpdate (tr_session               * session,
1450                 tr_variant                  * args_in UNUSED,
1451                 tr_variant                  * args_out UNUSED,
1452                 struct tr_rpc_idle_data  * idle_data)
1453{
1454    tr_webRun (session, session->blocklist_url, NULL, NULL, gotNewBlocklist, idle_data);
1455    return NULL;
1456}
1457
1458/***
1459****
1460***/
1461
1462static void
1463addTorrentImpl (struct tr_rpc_idle_data * data, tr_ctor * ctor)
1464{
1465    int err = 0;
1466    const char * result = NULL;
1467    tr_torrent * tor = tr_torrentNew (ctor, &err);
1468
1469    tr_ctorFree (ctor);
1470
1471    if (tor)
1472    {
1473        tr_variant fields;
1474        tr_variantInitList (&fields, 3);
1475        tr_variantListAddStr (&fields, "id");
1476        tr_variantListAddStr (&fields, "name");
1477        tr_variantListAddStr (&fields, "hashString");
1478        addInfo (tor, tr_variantDictAdd (data->args_out, TR_KEY_torrent_added), &fields);
1479        notify (data->session, TR_RPC_TORRENT_ADDED, tor);
1480        tr_variantFree (&fields);
1481    }
1482    else if (err == TR_PARSE_DUPLICATE)
1483    {
1484        result = "duplicate torrent";
1485    }
1486    else if (err == TR_PARSE_ERR)
1487    {
1488        result = "invalid or corrupt torrent file";
1489    }
1490
1491    tr_idle_function_done (data, result);
1492}
1493
1494
1495struct add_torrent_idle_data
1496{
1497    struct tr_rpc_idle_data * data;
1498    tr_ctor * ctor;
1499};
1500
1501static void
1502gotMetadataFromURL (tr_session       * session UNUSED,
1503                    bool               did_connect UNUSED,
1504                    bool               did_timeout UNUSED,
1505                    long               response_code,
1506                    const void       * response,
1507                    size_t             response_byte_count,
1508                    void             * user_data)
1509{
1510    struct add_torrent_idle_data * data = user_data;
1511
1512    dbgmsg ("torrentAdd: HTTP response code was %ld (%s); response length was %zu bytes",
1513            response_code, tr_webGetResponseStr (response_code), response_byte_count);
1514
1515    if (response_code==200 || response_code==221) /* http or ftp success.. */
1516    {
1517        tr_ctorSetMetainfo (data->ctor, response, response_byte_count);
1518        addTorrentImpl (data->data, data->ctor);
1519    }
1520    else
1521    {
1522        char result[1024];
1523        tr_snprintf (result, sizeof (result), "gotMetadataFromURL: http error %ld: %s",
1524                     response_code, tr_webGetResponseStr (response_code));
1525        tr_idle_function_done (data->data, result);
1526    }
1527
1528    tr_free (data);
1529}
1530
1531static bool
1532isCurlURL (const char * filename)
1533{
1534    if (filename == NULL)
1535        return false;
1536
1537    return !strncmp (filename, "ftp://", 6) ||
1538           !strncmp (filename, "http://", 7) ||
1539           !strncmp (filename, "https://", 8);
1540}
1541
1542static tr_file_index_t*
1543fileListFromList (tr_variant * list, tr_file_index_t * setmeCount)
1544{
1545    size_t i;
1546    const size_t childCount = tr_variantListSize (list);
1547    tr_file_index_t n = 0;
1548    tr_file_index_t * files = tr_new0 (tr_file_index_t, childCount);
1549
1550    for (i=0; i<childCount; ++i) {
1551        int64_t intVal;
1552        if (tr_variantGetInt (tr_variantListChild (list, i), &intVal))
1553            files[n++] = (tr_file_index_t)intVal;
1554    }
1555
1556    *setmeCount = n;
1557    return files;
1558}
1559
1560static const char*
1561torrentAdd (tr_session               * session,
1562            tr_variant                  * args_in,
1563            tr_variant                  * args_out UNUSED,
1564            struct tr_rpc_idle_data  * idle_data)
1565{
1566    const char * filename = NULL;
1567    const char * metainfo_base64 = NULL;
1568
1569    assert (idle_data != NULL);
1570
1571    tr_variantDictFindStr (args_in, TR_KEY_filename, &filename, NULL);
1572    tr_variantDictFindStr (args_in, TR_KEY_metainfo, &metainfo_base64, NULL);
1573    if (!filename && !metainfo_base64)
1574        return "no filename or metainfo specified";
1575    else
1576    {
1577        int64_t      i;
1578        bool         boolVal;
1579        tr_variant    * l;
1580        const char * str;
1581        const char * cookies = NULL;
1582        tr_ctor    * ctor = tr_ctorNew (session);
1583
1584        /* set the optional arguments */
1585
1586        tr_variantDictFindStr (args_in, TR_KEY_cookies, &cookies, NULL);
1587
1588        if (tr_variantDictFindStr (args_in, TR_KEY_download_dir, &str, NULL))
1589            tr_ctorSetDownloadDir (ctor, TR_FORCE, str);
1590
1591        if (tr_variantDictFindBool (args_in, TR_KEY_paused, &boolVal))
1592            tr_ctorSetPaused (ctor, TR_FORCE, boolVal);
1593
1594        if (tr_variantDictFindInt (args_in, TR_KEY_peer_limit, &i))
1595            tr_ctorSetPeerLimit (ctor, TR_FORCE, i);
1596
1597        if (tr_variantDictFindInt (args_in, TR_KEY_bandwidthPriority, &i))
1598            tr_ctorSetBandwidthPriority (ctor, i);
1599
1600        if (tr_variantDictFindList (args_in, TR_KEY_files_unwanted, &l)) {
1601            tr_file_index_t fileCount;
1602            tr_file_index_t * files = fileListFromList (l, &fileCount);
1603            tr_ctorSetFilesWanted (ctor, files, fileCount, false);
1604            tr_free (files);
1605        }
1606        if (tr_variantDictFindList (args_in, TR_KEY_files_wanted, &l)) {
1607            tr_file_index_t fileCount;
1608            tr_file_index_t * files = fileListFromList (l, &fileCount);
1609            tr_ctorSetFilesWanted (ctor, files, fileCount, true);
1610            tr_free (files);
1611        }
1612
1613        if (tr_variantDictFindList (args_in, TR_KEY_priority_low, &l)) {
1614            tr_file_index_t fileCount;
1615            tr_file_index_t * files = fileListFromList (l, &fileCount);
1616            tr_ctorSetFilePriorities (ctor, files, fileCount, TR_PRI_LOW);
1617            tr_free (files);
1618        }
1619        if (tr_variantDictFindList (args_in, TR_KEY_priority_normal, &l)) {
1620            tr_file_index_t fileCount;
1621            tr_file_index_t * files = fileListFromList (l, &fileCount);
1622            tr_ctorSetFilePriorities (ctor, files, fileCount, TR_PRI_NORMAL);
1623            tr_free (files);
1624        }
1625        if (tr_variantDictFindList (args_in, TR_KEY_priority_high, &l)) {
1626            tr_file_index_t fileCount;
1627            tr_file_index_t * files = fileListFromList (l, &fileCount);
1628            tr_ctorSetFilePriorities (ctor, files, fileCount, TR_PRI_HIGH);
1629            tr_free (files);
1630        }
1631
1632        dbgmsg ("torrentAdd: filename is \"%s\"", filename ? filename : " (null)");
1633
1634        if (isCurlURL (filename))
1635        {
1636            struct add_torrent_idle_data * d = tr_new0 (struct add_torrent_idle_data, 1);
1637            d->data = idle_data;
1638            d->ctor = ctor;
1639            tr_webRun (session, filename, NULL, cookies, gotMetadataFromURL, d);
1640        }
1641        else
1642        {
1643            char * fname = tr_strstrip (tr_strdup (filename));
1644
1645            if (fname == NULL)
1646            {
1647                int len;
1648                char * metainfo = tr_base64_decode (metainfo_base64, -1, &len);
1649                tr_ctorSetMetainfo (ctor, (uint8_t*)metainfo, len);
1650                tr_free (metainfo);
1651            }
1652            else if (!strncmp (fname, "magnet:?", 8))
1653            {
1654                tr_ctorSetMetainfoFromMagnetLink (ctor, fname);
1655            }
1656            else
1657            {
1658                tr_ctorSetMetainfoFromFile (ctor, fname);
1659            }
1660
1661            addTorrentImpl (idle_data, ctor);
1662
1663            tr_free (fname);
1664        }
1665
1666    }
1667
1668    return NULL;
1669}
1670
1671/***
1672****
1673***/
1674
1675static const char*
1676sessionSet (tr_session               * session,
1677            tr_variant                  * args_in,
1678            tr_variant                  * args_out UNUSED,
1679            struct tr_rpc_idle_data  * idle_data UNUSED)
1680{
1681    int64_t      i;
1682    double       d;
1683    bool         boolVal;
1684    const char * str;
1685
1686    assert (idle_data == NULL);
1687
1688    if (tr_variantDictFindInt (args_in, TR_KEY_cache_size_mb, &i))
1689        tr_sessionSetCacheLimit_MB (session, i);
1690    if (tr_variantDictFindInt (args_in, TR_KEY_alt_speed_up, &i))
1691        tr_sessionSetAltSpeed_KBps (session, TR_UP, i);
1692    if (tr_variantDictFindInt (args_in, TR_KEY_alt_speed_down, &i))
1693        tr_sessionSetAltSpeed_KBps (session, TR_DOWN, i);
1694    if (tr_variantDictFindBool (args_in, TR_KEY_alt_speed_enabled, &boolVal))
1695        tr_sessionUseAltSpeed (session, boolVal);
1696    if (tr_variantDictFindInt (args_in, TR_KEY_alt_speed_time_begin, &i))
1697        tr_sessionSetAltSpeedBegin (session, i);
1698    if (tr_variantDictFindInt (args_in, TR_KEY_alt_speed_time_end, &i))
1699        tr_sessionSetAltSpeedEnd (session, i);
1700    if (tr_variantDictFindInt (args_in, TR_KEY_alt_speed_time_day, &i))
1701        tr_sessionSetAltSpeedDay (session, i);
1702    if (tr_variantDictFindBool (args_in, TR_KEY_alt_speed_time_enabled, &boolVal))
1703        tr_sessionUseAltSpeedTime (session, boolVal);
1704    if (tr_variantDictFindBool (args_in, TR_KEY_blocklist_enabled, &boolVal))
1705        tr_blocklistSetEnabled (session, boolVal);
1706    if (tr_variantDictFindStr (args_in, TR_KEY_blocklist_url, &str, NULL))
1707        tr_blocklistSetURL (session, str);
1708    if (tr_variantDictFindStr (args_in, TR_KEY_download_dir, &str, NULL))
1709        tr_sessionSetDownloadDir (session, str);
1710    if (tr_variantDictFindInt (args_in, TR_KEY_queue_stalled_minutes, &i))
1711        tr_sessionSetQueueStalledMinutes (session, i);
1712    if (tr_variantDictFindBool (args_in, TR_KEY_queue_stalled_enabled, &boolVal))
1713        tr_sessionSetQueueStalledEnabled (session, boolVal);
1714    if (tr_variantDictFindInt (args_in, TR_KEY_download_queue_size, &i))
1715        tr_sessionSetQueueSize (session, TR_DOWN, i);
1716    if (tr_variantDictFindBool (args_in, TR_KEY_download_queue_enabled, &boolVal))
1717        tr_sessionSetQueueEnabled (session, TR_DOWN, boolVal);
1718    if (tr_variantDictFindStr (args_in, TR_KEY_incomplete_dir, &str, NULL))
1719        tr_sessionSetIncompleteDir (session, str);
1720    if (tr_variantDictFindBool (args_in, TR_KEY_incomplete_dir_enabled, &boolVal))
1721        tr_sessionSetIncompleteDirEnabled (session, boolVal);
1722    if (tr_variantDictFindInt (args_in, TR_KEY_peer_limit_global, &i))
1723        tr_sessionSetPeerLimit (session, i);
1724    if (tr_variantDictFindInt (args_in, TR_KEY_peer_limit_per_torrent, &i))
1725        tr_sessionSetPeerLimitPerTorrent (session, i);
1726    if (tr_variantDictFindBool (args_in, TR_KEY_pex_enabled, &boolVal))
1727        tr_sessionSetPexEnabled (session, boolVal);
1728    if (tr_variantDictFindBool (args_in, TR_KEY_dht_enabled, &boolVal))
1729        tr_sessionSetDHTEnabled (session, boolVal);
1730    if (tr_variantDictFindBool (args_in, TR_KEY_utp_enabled, &boolVal))
1731        tr_sessionSetUTPEnabled (session, boolVal);
1732    if (tr_variantDictFindBool (args_in, TR_KEY_lpd_enabled, &boolVal))
1733        tr_sessionSetLPDEnabled (session, boolVal);
1734    if (tr_variantDictFindBool (args_in, TR_KEY_peer_port_random_on_start, &boolVal))
1735        tr_sessionSetPeerPortRandomOnStart (session, boolVal);
1736    if (tr_variantDictFindInt (args_in, TR_KEY_peer_port, &i))
1737        tr_sessionSetPeerPort (session, i);
1738    if (tr_variantDictFindBool (args_in, TR_KEY_port_forwarding_enabled, &boolVal))
1739        tr_sessionSetPortForwardingEnabled (session, boolVal);
1740    if (tr_variantDictFindBool (args_in, TR_KEY_rename_partial_files, &boolVal))
1741        tr_sessionSetIncompleteFileNamingEnabled (session, boolVal);
1742    if (tr_variantDictFindReal (args_in, TR_KEY_seedRatioLimit, &d))
1743        tr_sessionSetRatioLimit (session, d);
1744    if (tr_variantDictFindBool (args_in, TR_KEY_seedRatioLimited, &boolVal))
1745        tr_sessionSetRatioLimited (session, boolVal);
1746    if (tr_variantDictFindInt (args_in, TR_KEY_idle_seeding_limit, &i))
1747        tr_sessionSetIdleLimit (session, i);
1748    if (tr_variantDictFindBool (args_in, TR_KEY_idle_seeding_limit_enabled, &boolVal))
1749        tr_sessionSetIdleLimited (session, boolVal);
1750    if (tr_variantDictFindBool (args_in, TR_KEY_start_added_torrents, &boolVal))
1751        tr_sessionSetPaused (session, !boolVal);
1752    if (tr_variantDictFindBool (args_in, TR_KEY_seed_queue_enabled, &boolVal))
1753        tr_sessionSetQueueEnabled (session, TR_UP, boolVal);
1754    if (tr_variantDictFindInt (args_in, TR_KEY_seed_queue_size, &i))
1755        tr_sessionSetQueueSize (session, TR_UP, i);
1756    if (tr_variantDictFindStr (args_in, TR_KEY_script_torrent_done_filename, &str, NULL))
1757        tr_sessionSetTorrentDoneScript (session, str);
1758    if (tr_variantDictFindBool (args_in, TR_KEY_script_torrent_done_enabled, &boolVal))
1759        tr_sessionSetTorrentDoneScriptEnabled (session, boolVal);
1760    if (tr_variantDictFindBool (args_in, TR_KEY_trash_original_torrent_files, &boolVal))
1761        tr_sessionSetDeleteSource (session, boolVal);
1762    if (tr_variantDictFindInt (args_in, TR_KEY_speed_limit_down, &i))
1763        tr_sessionSetSpeedLimit_KBps (session, TR_DOWN, i);
1764    if (tr_variantDictFindBool (args_in, TR_KEY_speed_limit_down_enabled, &boolVal))
1765        tr_sessionLimitSpeed (session, TR_DOWN, boolVal);
1766    if (tr_variantDictFindInt (args_in, TR_KEY_speed_limit_up, &i))
1767        tr_sessionSetSpeedLimit_KBps (session, TR_UP, i);
1768    if (tr_variantDictFindBool (args_in, TR_KEY_speed_limit_up_enabled, &boolVal))
1769        tr_sessionLimitSpeed (session, TR_UP, boolVal);
1770    if (tr_variantDictFindStr (args_in, TR_KEY_encryption, &str, NULL)) {
1771        if (!strcmp (str, "required"))
1772            tr_sessionSetEncryption (session, TR_ENCRYPTION_REQUIRED);
1773        else if (!strcmp (str, "tolerated"))
1774            tr_sessionSetEncryption (session, TR_CLEAR_PREFERRED);
1775        else
1776            tr_sessionSetEncryption (session, TR_ENCRYPTION_PREFERRED);
1777    }
1778
1779    notify (session, TR_RPC_SESSION_CHANGED, NULL);
1780
1781    return NULL;
1782}
1783
1784static const char*
1785sessionStats (tr_session               * session,
1786              tr_variant                  * args_in UNUSED,
1787              tr_variant                  * args_out,
1788              struct tr_rpc_idle_data  * idle_data UNUSED)
1789{
1790    int running = 0;
1791    int total = 0;
1792    tr_variant * d;
1793    tr_session_stats currentStats = { 0.0f, 0, 0, 0, 0, 0 };
1794    tr_session_stats cumulativeStats = { 0.0f, 0, 0, 0, 0, 0 };
1795    tr_torrent * tor = NULL;
1796
1797    assert (idle_data == NULL);
1798
1799    while ((tor = tr_torrentNext (session, tor))) {
1800        ++total;
1801        if (tor->isRunning)
1802            ++running;
1803    }
1804
1805    tr_sessionGetStats (session, &currentStats);
1806    tr_sessionGetCumulativeStats (session, &cumulativeStats);
1807
1808    tr_variantDictAddInt  (args_out, TR_KEY_activeTorrentCount, running);
1809    tr_variantDictAddReal (args_out, TR_KEY_downloadSpeed, tr_sessionGetPieceSpeed_Bps (session, TR_DOWN));
1810    tr_variantDictAddInt  (args_out, TR_KEY_pausedTorrentCount, total - running);
1811    tr_variantDictAddInt  (args_out, TR_KEY_torrentCount, total);
1812    tr_variantDictAddReal (args_out, TR_KEY_uploadSpeed, tr_sessionGetPieceSpeed_Bps (session, TR_UP));
1813
1814    d = tr_variantDictAddDict (args_out, TR_KEY_cumulative_stats, 5);
1815    tr_variantDictAddInt (d, TR_KEY_downloadedBytes, cumulativeStats.downloadedBytes);
1816    tr_variantDictAddInt (d, TR_KEY_filesAdded, cumulativeStats.filesAdded);
1817    tr_variantDictAddInt (d, TR_KEY_secondsActive, cumulativeStats.secondsActive);
1818    tr_variantDictAddInt (d, TR_KEY_sessionCount, cumulativeStats.sessionCount);
1819    tr_variantDictAddInt (d, TR_KEY_uploadedBytes, cumulativeStats.uploadedBytes);
1820
1821    d = tr_variantDictAddDict (args_out, TR_KEY_current_stats, 5);
1822    tr_variantDictAddInt (d, TR_KEY_downloadedBytes, currentStats.downloadedBytes);
1823    tr_variantDictAddInt (d, TR_KEY_filesAdded, currentStats.filesAdded);
1824    tr_variantDictAddInt (d, TR_KEY_secondsActive, currentStats.secondsActive);
1825    tr_variantDictAddInt (d, TR_KEY_sessionCount, currentStats.sessionCount);
1826    tr_variantDictAddInt (d, TR_KEY_uploadedBytes, currentStats.uploadedBytes);
1827
1828    return NULL;
1829}
1830
1831static const char*
1832sessionGet (tr_session               * s,
1833            tr_variant                  * args_in UNUSED,
1834            tr_variant                  * args_out,
1835            struct tr_rpc_idle_data  * idle_data UNUSED)
1836{
1837    const char * str;
1838    tr_variant * d = args_out;
1839
1840    assert (idle_data == NULL);
1841    tr_variantDictAddInt  (d, TR_KEY_alt_speed_up, tr_sessionGetAltSpeed_KBps (s,TR_UP));
1842    tr_variantDictAddInt  (d, TR_KEY_alt_speed_down, tr_sessionGetAltSpeed_KBps (s,TR_DOWN));
1843    tr_variantDictAddBool (d, TR_KEY_alt_speed_enabled, tr_sessionUsesAltSpeed (s));
1844    tr_variantDictAddInt  (d, TR_KEY_alt_speed_time_begin, tr_sessionGetAltSpeedBegin (s));
1845    tr_variantDictAddInt  (d, TR_KEY_alt_speed_time_end,tr_sessionGetAltSpeedEnd (s));
1846    tr_variantDictAddInt  (d, TR_KEY_alt_speed_time_day,tr_sessionGetAltSpeedDay (s));
1847    tr_variantDictAddBool (d, TR_KEY_alt_speed_time_enabled, tr_sessionUsesAltSpeedTime (s));
1848    tr_variantDictAddBool (d, TR_KEY_blocklist_enabled, tr_blocklistIsEnabled (s));
1849    tr_variantDictAddStr  (d, TR_KEY_blocklist_url, tr_blocklistGetURL (s));
1850    tr_variantDictAddInt  (d, TR_KEY_cache_size_mb, tr_sessionGetCacheLimit_MB (s));
1851    tr_variantDictAddInt  (d, TR_KEY_blocklist_size, tr_blocklistGetRuleCount (s));
1852    tr_variantDictAddStr  (d, TR_KEY_config_dir, tr_sessionGetConfigDir (s));
1853    tr_variantDictAddStr  (d, TR_KEY_download_dir, tr_sessionGetDownloadDir (s));
1854    tr_variantDictAddBool (d, TR_KEY_download_queue_enabled, tr_sessionGetQueueEnabled (s, TR_DOWN));
1855    tr_variantDictAddInt  (d, TR_KEY_download_queue_size, tr_sessionGetQueueSize (s, TR_DOWN));
1856    tr_variantDictAddInt  (d, TR_KEY_download_dir_free_space,  tr_sessionGetDownloadDirFreeSpace (s));
1857    tr_variantDictAddInt  (d, TR_KEY_peer_limit_global, tr_sessionGetPeerLimit (s));
1858    tr_variantDictAddInt  (d, TR_KEY_peer_limit_per_torrent, tr_sessionGetPeerLimitPerTorrent (s));
1859    tr_variantDictAddStr  (d, TR_KEY_incomplete_dir, tr_sessionGetIncompleteDir (s));
1860    tr_variantDictAddBool (d, TR_KEY_incomplete_dir_enabled, tr_sessionIsIncompleteDirEnabled (s));
1861    tr_variantDictAddBool (d, TR_KEY_pex_enabled, tr_sessionIsPexEnabled (s));
1862    tr_variantDictAddBool (d, TR_KEY_utp_enabled, tr_sessionIsUTPEnabled (s));
1863    tr_variantDictAddBool (d, TR_KEY_dht_enabled, tr_sessionIsDHTEnabled (s));
1864    tr_variantDictAddBool (d, TR_KEY_ldp_enabled, tr_sessionIsLPDEnabled (s));
1865    tr_variantDictAddInt  (d, TR_KEY_peer_port, tr_sessionGetPeerPort (s));
1866    tr_variantDictAddBool (d, TR_KEY_peer_port_random_on_start, tr_sessionGetPeerPortRandomOnStart (s));
1867    tr_variantDictAddBool (d, TR_KEY_port_forwarding_enabled, tr_sessionIsPortForwardingEnabled (s));
1868    tr_variantDictAddBool (d, TR_KEY_rename_partial_files, tr_sessionIsIncompleteFileNamingEnabled (s));
1869    tr_variantDictAddInt  (d, TR_KEY_rpc_version, RPC_VERSION);
1870    tr_variantDictAddInt  (d, TR_KEY_rpc_version_minimum, RPC_VERSION_MIN);
1871    tr_variantDictAddReal (d, TR_KEY_seedRatioLimit, tr_sessionGetRatioLimit (s));
1872    tr_variantDictAddBool (d, TR_KEY_seedRatioLimited, tr_sessionIsRatioLimited (s));
1873    tr_variantDictAddInt  (d, TR_KEY_idle_seeding_limit, tr_sessionGetIdleLimit (s));
1874    tr_variantDictAddBool (d, TR_KEY_idle_seeding_limit_enabled, tr_sessionIsIdleLimited (s));
1875    tr_variantDictAddBool (d, TR_KEY_seed_queue_enabled, tr_sessionGetQueueEnabled (s, TR_UP));
1876    tr_variantDictAddInt  (d, TR_KEY_seed_queue_size, tr_sessionGetQueueSize (s, TR_UP));
1877    tr_variantDictAddBool (d, TR_KEY_start_added_torrents, !tr_sessionGetPaused (s));
1878    tr_variantDictAddBool (d, TR_KEY_trash_original_torrent_files, tr_sessionGetDeleteSource (s));
1879    tr_variantDictAddInt  (d, TR_KEY_speed_limit_up, tr_sessionGetSpeedLimit_KBps (s, TR_UP));
1880    tr_variantDictAddBool (d, TR_KEY_speed_limit_up_enabled, tr_sessionIsSpeedLimited (s, TR_UP));
1881    tr_variantDictAddInt  (d, TR_KEY_speed_limit_down, tr_sessionGetSpeedLimit_KBps (s, TR_DOWN));
1882    tr_variantDictAddBool (d, TR_KEY_speed_limit_down_enabled, tr_sessionIsSpeedLimited (s, TR_DOWN));
1883    tr_variantDictAddStr  (d, TR_KEY_script_torrent_done_filename, tr_sessionGetTorrentDoneScript (s));
1884    tr_variantDictAddBool (d, TR_KEY_script_torrent_done_enabled, tr_sessionIsTorrentDoneScriptEnabled (s));
1885    tr_variantDictAddBool (d, TR_KEY_queue_stalled_enabled, tr_sessionGetQueueStalledEnabled (s));
1886    tr_variantDictAddInt  (d, TR_KEY_queue_stalled_minutes, tr_sessionGetQueueStalledMinutes (s));
1887    tr_formatter_get_units (tr_variantDictAddDict (d, TR_KEY_units, 0));
1888    tr_variantDictAddStr  (d, TR_KEY_version, LONG_VERSION_STRING);
1889    switch (tr_sessionGetEncryption (s)) {
1890        case TR_CLEAR_PREFERRED: str = "tolerated"; break;
1891        case TR_ENCRYPTION_REQUIRED: str = "required"; break;
1892        default: str = "preferred"; break;
1893    }
1894    tr_variantDictAddStr (d, TR_KEY_encryption, str);
1895
1896    return NULL;
1897}
1898
1899/***
1900****
1901***/
1902
1903static const char*
1904sessionClose (tr_session               * session,
1905              tr_variant                  * args_in UNUSED,
1906              tr_variant                  * args_out UNUSED,
1907              struct tr_rpc_idle_data  * idle_data UNUSED)
1908{
1909    notify (session, TR_RPC_SESSION_CLOSE, NULL);
1910    return NULL;
1911}
1912
1913/***
1914****
1915***/
1916
1917typedef const char* (*handler)(tr_session*, tr_variant*, tr_variant*, struct tr_rpc_idle_data *);
1918
1919static struct method
1920{
1921    const char *  name;
1922    bool          immediate;
1923    handler       func;
1924}
1925methods[] =
1926{
1927    { "port-test",             false, portTest            },
1928    { "blocklist-update",      false, blocklistUpdate     },
1929    { "session-close",         true,  sessionClose        },
1930    { "session-get",           true,  sessionGet          },
1931    { "session-set",           true,  sessionSet          },
1932    { "session-stats",         true,  sessionStats        },
1933    { "torrent-add",           false, torrentAdd          },
1934    { "torrent-get",           true,  torrentGet          },
1935    { "torrent-remove",        true,  torrentRemove       },
1936    { "torrent-set",           true,  torrentSet          },
1937    { "torrent-set-location",  true,  torrentSetLocation  },
1938    { "torrent-start",         true,  torrentStart        },
1939    { "torrent-start-now",     true,  torrentStartNow     },
1940    { "torrent-stop",          true,  torrentStop         },
1941    { "torrent-verify",        true,  torrentVerify       },
1942    { "torrent-reannounce",    true,  torrentReannounce   },
1943    { "queue-move-top",        true,  queueMoveTop        },
1944    { "queue-move-up",         true,  queueMoveUp         },
1945    { "queue-move-down",       true,  queueMoveDown       },
1946    { "queue-move-bottom",     true,  queueMoveBottom     }
1947};
1948
1949static void
1950noop_response_callback (tr_session       * session UNUSED,
1951                        struct evbuffer  * response UNUSED,
1952                        void             * user_data UNUSED)
1953{
1954}
1955
1956static void
1957request_exec (tr_session             * session,
1958              tr_variant             * request,
1959              tr_rpc_response_func     callback,
1960              void                   * callback_user_data)
1961{
1962    int i;
1963    const char * str;
1964    tr_variant * args_in = tr_variantDictFind (request, TR_KEY_arguments);
1965    const char * result = NULL;
1966
1967    if (callback == NULL)
1968        callback = noop_response_callback;
1969
1970    /* parse the request */
1971    if (!tr_variantDictFindStr (request, TR_KEY_method, &str, NULL))
1972        result = "no method name";
1973    else {
1974        const int n = TR_N_ELEMENTS (methods);
1975        for (i = 0; i < n; ++i)
1976            if (!strcmp (str, methods[i].name))
1977                break;
1978        if (i ==n)
1979            result = "method name not recognized";
1980    }
1981
1982    /* if we couldn't figure out which method to use, return an error */
1983    if (result != NULL)
1984    {
1985        int64_t tag;
1986        tr_variant response;
1987        struct evbuffer * buf;
1988
1989        tr_variantInitDict (&response, 3);
1990        tr_variantDictAddDict (&response, TR_KEY_arguments, 0);
1991        tr_variantDictAddStr (&response, TR_KEY_result, result);
1992        if (tr_variantDictFindInt (request, TR_KEY_tag, &tag))
1993            tr_variantDictAddInt (&response, TR_KEY_tag, tag);
1994
1995        buf = tr_variantToBuf (&response, TR_VARIANT_FMT_JSON_LEAN);
1996      (*callback)(session, buf, callback_user_data);
1997        evbuffer_free (buf);
1998
1999        tr_variantFree (&response);
2000    }
2001    else if (methods[i].immediate)
2002    {
2003        int64_t tag;
2004        tr_variant response;
2005        tr_variant * args_out;
2006        struct evbuffer * buf;
2007
2008        tr_variantInitDict (&response, 3);
2009        args_out = tr_variantDictAddDict (&response, TR_KEY_arguments, 0);
2010        result = (*methods[i].func)(session, args_in, args_out, NULL);
2011        if (result == NULL)
2012            result = "success";
2013        tr_variantDictAddStr (&response, TR_KEY_result, result);
2014        if (tr_variantDictFindInt (request, TR_KEY_tag, &tag))
2015            tr_variantDictAddInt (&response, TR_KEY_tag, tag);
2016
2017        buf = tr_variantToBuf (&response, TR_VARIANT_FMT_JSON_LEAN);
2018      (*callback)(session, buf, callback_user_data);
2019        evbuffer_free (buf);
2020
2021        tr_variantFree (&response);
2022    }
2023    else
2024    {
2025        int64_t tag;
2026        struct tr_rpc_idle_data * data = tr_new0 (struct tr_rpc_idle_data, 1);
2027        data->session = session;
2028        data->response = tr_new0 (tr_variant, 1);
2029        tr_variantInitDict (data->response, 3);
2030        if (tr_variantDictFindInt (request, TR_KEY_tag, &tag))
2031            tr_variantDictAddInt (data->response, TR_KEY_tag, tag);
2032        data->args_out = tr_variantDictAddDict (data->response, TR_KEY_arguments, 0);
2033        data->callback = callback;
2034        data->callback_user_data = callback_user_data;
2035      (*methods[i].func)(session, args_in, data->args_out, data);
2036    }
2037}
2038
2039void
2040tr_rpc_request_exec_json (tr_session            * session,
2041                          const void            * request_json,
2042                          int                     request_len,
2043                          tr_rpc_response_func    callback,
2044                          void                  * callback_user_data)
2045{
2046    tr_variant top;
2047    int have_content;
2048
2049    if (request_len < 0)
2050        request_len = strlen (request_json);
2051
2052    have_content = !tr_variantFromJson (&top, request_json, request_len);
2053    request_exec (session, have_content ? &top : NULL, callback, callback_user_data);
2054
2055    if (have_content)
2056        tr_variantFree (&top);
2057}
2058
2059/**
2060 * Munge the URI into a usable form.
2061 *
2062 * We have very loose typing on this to make the URIs as simple as possible:
2063 * - anything not a 'tag' or 'method' is automatically in 'arguments'
2064 * - values that are all-digits are numbers
2065 * - values that are all-digits or commas are number lists
2066 * - all other values are strings
2067 */
2068void
2069tr_rpc_parse_list_str (tr_variant  * setme,
2070                       const char  * str,
2071                       int           len)
2072
2073{
2074    int valueCount;
2075    int * values = tr_parseNumberRange (str, len, &valueCount);
2076
2077    if (valueCount == 0)
2078        tr_variantInitStr (setme, str, len);
2079    else if (valueCount == 1)
2080        tr_variantInitInt (setme, values[0]);
2081    else {
2082        int i;
2083        tr_variantInitList (setme, valueCount);
2084        for (i=0; i<valueCount; ++i)
2085            tr_variantListAddInt (setme, values[i]);
2086    }
2087
2088    tr_free (values);
2089}
2090
2091void
2092tr_rpc_request_exec_uri (tr_session           * session,
2093                         const void           * request_uri,
2094                         int                    request_len,
2095                         tr_rpc_response_func   callback,
2096                         void                 * callback_user_data)
2097{
2098    tr_variant      top, * args;
2099    char *       request = tr_strndup (request_uri, request_len);
2100    const char * pch;
2101
2102    tr_variantInitDict (&top, 3);
2103    args = tr_variantDictAddDict (&top, TR_KEY_arguments, 0);
2104
2105    pch = strchr (request, '?');
2106    if (!pch) pch = request;
2107    while (pch)
2108    {
2109        const char * delim = strchr (pch, '=');
2110        const char * next = strchr (pch, '&');
2111        if (delim)
2112        {
2113            char *    key = tr_strndup (pch, delim - pch);
2114            int       isArg = strcmp (key, "method") && strcmp (key, "tag");
2115            tr_variant * parent = isArg ? args : &top;
2116            tr_rpc_parse_list_str (tr_variantDictAdd (parent, tr_quark_new (key, delim-pch)),
2117                                  delim + 1,
2118                                  next ? (size_t)(
2119                                       next -
2120                                    (delim + 1)) : strlen (delim + 1));
2121            tr_free (key);
2122        }
2123        pch = next ? next + 1 : NULL;
2124    }
2125
2126    request_exec (session, &top, callback, callback_user_data);
2127
2128    /* cleanup */
2129    tr_variantFree (&top);
2130    tr_free (request);
2131}
Note: See TracBrowser for help on using the repository browser.