source: trunk/libtransmission/rpcimpl.c @ 14192

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

fix a handful of CL warnings & errors in libtransmission. mikedld

  • Property svn:keywords set to Date Rev Author Id
File size: 71.4 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 14192 2013-09-08 17:58:14Z 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
19#ifdef HAVE_ZLIB
20 #include <zlib.h>
21#endif
22
23#include <event2/buffer.h>
24
25#include "transmission.h"
26#include "completion.h"
27#include "fdlimit.h"
28#include "log.h"
29#include "platform-quota.h" /* tr_device_info_get_free_space() */
30#include "rpcimpl.h"
31#include "session.h"
32#include "torrent.h"
33#include "utils.h"
34#include "variant.h"
35#include "version.h"
36#include "web.h"
37
38#define RPC_VERSION     15
39#define RPC_VERSION_MIN 1
40
41#define RECENTLY_ACTIVE_SECONDS 60
42
43#define TR_N_ELEMENTS(ary)(sizeof (ary) / sizeof (*ary))
44
45#if 0
46#define dbgmsg(fmt, ...) \
47    do { \
48        fprintf (stderr, "%s:%d"#fmt, __FILE__, __LINE__, __VA_ARGS__); \
49        fprintf (stderr, "\n"); \
50    } while (0)
51#else
52#define dbgmsg(...) \
53  do \
54    { \
55      if (tr_logGetDeepEnabled ()) \
56        tr_logAddDeep (__FILE__, __LINE__, "RPC", __VA_ARGS__); \
57    } \
58  while (0)
59#endif
60
61
62/***
63****
64***/
65
66static tr_rpc_callback_status
67notify (tr_session * session,
68        int          type,
69        tr_torrent * tor)
70{
71    tr_rpc_callback_status status = 0;
72
73    if (session->rpc_func)
74        status = session->rpc_func (session, type, tor,
75                                    session->rpc_func_user_data);
76
77    return status;
78}
79
80/***
81****
82***/
83
84/* For functions that can't be immediately executed, like torrentAdd,
85 * this is the callback data used to pass a response to the caller
86 * when the task is complete */
87struct tr_rpc_idle_data
88{
89 tr_session            * session;
90 tr_variant            * response;
91 tr_variant            * args_out;
92 tr_rpc_response_func    callback;
93 void                  * callback_user_data;
94};
95
96static void
97tr_idle_function_done (struct tr_rpc_idle_data * data, const char * result)
98{
99 struct evbuffer * buf;
100
101 if (result == NULL)
102   result = "success";
103 tr_variantDictAddStr (data->response, TR_KEY_result, result);
104
105 buf = tr_variantToBuf (data->response, TR_VARIANT_FMT_JSON_LEAN);
106 (*data->callback)(data->session, buf, data->callback_user_data);
107 evbuffer_free (buf);
108
109 tr_variantFree (data->response);
110 tr_free (data->response);
111 tr_free (data);
112}
113
114/***
115****
116***/
117
118static tr_torrent **
119getTorrents (tr_session * session,
120             tr_variant * args,
121             int        * setmeCount)
122{
123  int torrentCount = 0;
124  int64_t id;
125  tr_torrent ** torrents = NULL;
126  tr_variant * ids;
127  const char * str;
128
129  if (tr_variantDictFindList (args, TR_KEY_ids, &ids))
130    {
131      int i;
132      const int n = tr_variantListSize (ids);
133
134      torrents = tr_new0 (tr_torrent *, n);
135
136      for (i=0; i<n; ++i)
137        {
138          const char * str;
139          tr_torrent * tor;
140          tr_variant * node = tr_variantListChild (ids, i);
141
142          if (tr_variantGetInt (node, &id))
143            tor = tr_torrentFindFromId (session, id);
144          else if (tr_variantGetStr (node, &str, NULL))
145            tor = tr_torrentFindFromHashString (session, str);
146          else
147            tor = NULL;
148
149          if (tor != NULL)
150            torrents[torrentCount++] = tor;
151        }
152    }
153  else if (tr_variantDictFindInt (args, TR_KEY_ids, &id)
154        || tr_variantDictFindInt (args, TR_KEY_id, &id))
155    {
156      tr_torrent * tor;
157      torrents = tr_new0 (tr_torrent *, 1);
158      if ((tor = tr_torrentFindFromId (session, id)))
159        torrents[torrentCount++] = tor;
160    }
161  else if (tr_variantDictFindStr (args, TR_KEY_ids, &str, NULL))
162    {
163      if (!strcmp (str, "recently-active"))
164        {
165          tr_torrent * tor = NULL;
166          const time_t now = tr_time ();
167          const time_t window = RECENTLY_ACTIVE_SECONDS;
168          const int n = tr_sessionCountTorrents (session);
169          torrents = tr_new0 (tr_torrent *, n);
170          while ((tor = tr_torrentNext (session, tor)))
171            if (tor->anyDate >= now - window)
172              torrents[torrentCount++] = tor;
173        }
174      else
175        {
176          tr_torrent * tor;
177          torrents = tr_new0 (tr_torrent *, 1);
178          if ((tor = tr_torrentFindFromHashString (session, str)))
179            torrents[torrentCount++] = tor;
180        }
181    }
182  else /* all of them */
183    {
184      torrents = tr_sessionGetTorrents (session, &torrentCount);
185    }
186
187  *setmeCount = torrentCount;
188  return torrents;
189}
190
191static void
192notifyBatchQueueChange (tr_session * session, tr_torrent ** torrents, int n)
193{
194  int i;
195  for (i=0; i<n; ++i)
196    notify (session, TR_RPC_TORRENT_CHANGED, torrents[i]);
197  notify (session, TR_RPC_SESSION_QUEUE_POSITIONS_CHANGED, NULL);
198}
199
200static const char*
201queueMoveTop (tr_session               * session,
202              tr_variant               * args_in,
203              tr_variant               * args_out UNUSED,
204              struct tr_rpc_idle_data  * idle_data UNUSED)
205{
206  int n;
207  tr_torrent ** torrents = getTorrents (session, args_in, &n);
208  tr_torrentsQueueMoveTop (torrents, n);
209  notifyBatchQueueChange (session, torrents, n);
210  tr_free (torrents);
211  return NULL;
212}
213
214static const char*
215queueMoveUp (tr_session               * session,
216             tr_variant               * args_in,
217             tr_variant               * args_out UNUSED,
218             struct tr_rpc_idle_data  * idle_data UNUSED)
219{
220  int n;
221  tr_torrent ** torrents = getTorrents (session, args_in, &n);
222  tr_torrentsQueueMoveUp (torrents, n);
223  notifyBatchQueueChange (session, torrents, n);
224  tr_free (torrents);
225  return NULL;
226}
227
228static const char*
229queueMoveDown (tr_session               * session,
230               tr_variant               * args_in,
231               tr_variant               * args_out UNUSED,
232               struct tr_rpc_idle_data  * idle_data UNUSED)
233{
234  int n;
235  tr_torrent ** torrents = getTorrents (session, args_in, &n);
236  tr_torrentsQueueMoveDown (torrents, n);
237  notifyBatchQueueChange (session, torrents, n);
238  tr_free (torrents);
239  return NULL;
240}
241
242static const char*
243queueMoveBottom (tr_session               * session,
244                 tr_variant               * args_in,
245                 tr_variant               * args_out UNUSED,
246                 struct tr_rpc_idle_data  * idle_data UNUSED)
247{
248  int n;
249  tr_torrent ** torrents = getTorrents (session, args_in, &n);
250  tr_torrentsQueueMoveBottom (torrents, n);
251  notifyBatchQueueChange (session, torrents, n);
252  tr_free (torrents);
253  return NULL;
254}
255
256static const char*
257torrentStart (tr_session               * session,
258              tr_variant               * args_in,
259              tr_variant               * args_out UNUSED,
260              struct tr_rpc_idle_data  * idle_data UNUSED)
261{
262  int i;
263  int torrentCount;
264  tr_torrent ** torrents;
265
266  assert (idle_data == NULL);
267
268  torrents = getTorrents (session, args_in, &torrentCount);
269  for (i=0; i<torrentCount; ++i)
270    {
271      tr_torrent * tor = torrents[i];
272      if (!tor->isRunning)
273        {
274          tr_torrentStart (tor);
275          notify (session, TR_RPC_TORRENT_STARTED, tor);
276        }
277    }
278
279  tr_free (torrents);
280  return NULL;
281}
282
283static const char*
284torrentStartNow (tr_session               * session,
285                 tr_variant               * args_in,
286                 tr_variant               * args_out UNUSED,
287                 struct tr_rpc_idle_data  * idle_data UNUSED)
288{
289  int i;
290  int torrentCount;
291  tr_torrent ** torrents;
292
293  assert (idle_data == NULL);
294
295  torrents = getTorrents (session, args_in, &torrentCount);
296  for (i=0; i<torrentCount; ++i)
297    {
298      tr_torrent * tor = torrents[i];
299
300      if (!tor->isRunning)
301        {
302          tr_torrentStartNow (tor);
303          notify (session, TR_RPC_TORRENT_STARTED, tor);
304        }
305    }
306
307  tr_free (torrents);
308  return NULL;
309}
310
311static const char*
312torrentStop (tr_session               * session,
313             tr_variant               * args_in,
314             tr_variant               * args_out UNUSED,
315             struct tr_rpc_idle_data  * idle_data UNUSED)
316{
317  int i;
318  int torrentCount;
319  tr_torrent ** torrents;
320
321  assert (idle_data == NULL);
322
323  torrents = getTorrents (session, args_in, &torrentCount);
324  for (i=0; i<torrentCount; ++i)
325    {
326      tr_torrent * tor = torrents[i];
327
328      if (tor->isRunning || tr_torrentIsQueued (tor))
329        {
330          tor->isStopping = true;
331          notify (session, TR_RPC_TORRENT_STOPPED, tor);
332        }
333    }
334
335  tr_free (torrents);
336  return NULL;
337}
338
339static const char*
340torrentRemove (tr_session               * session,
341               tr_variant               * args_in,
342               tr_variant               * args_out UNUSED,
343               struct tr_rpc_idle_data  * idle_data UNUSED)
344{
345  int i;
346  int torrentCount;
347  tr_rpc_callback_type type;
348  bool deleteFlag = false;
349  tr_torrent ** torrents;
350
351  assert (idle_data == NULL);
352
353  tr_variantDictFindBool (args_in, TR_KEY_delete_local_data, &deleteFlag);
354  type = deleteFlag ? TR_RPC_TORRENT_TRASHING
355                    : TR_RPC_TORRENT_REMOVING;
356
357  torrents = getTorrents (session, args_in, &torrentCount);
358  for (i=0; i<torrentCount; ++i)
359    {
360      tr_torrent * tor = torrents[i];
361      const tr_rpc_callback_status status = notify (session, type, tor);
362
363      if (!(status & TR_RPC_NOREMOVE))
364        tr_torrentRemove (tor, deleteFlag, NULL);
365    }
366
367  tr_free (torrents);
368  return NULL;
369}
370
371static const char*
372torrentReannounce (tr_session               * session,
373                   tr_variant               * args_in,
374                   tr_variant               * args_out UNUSED,
375                   struct tr_rpc_idle_data  * idle_data UNUSED)
376{
377  int i;
378  int torrentCount;
379  tr_torrent ** torrents;
380
381  assert (idle_data == NULL);
382
383  torrents = getTorrents (session, args_in, &torrentCount);
384  for (i=0; i<torrentCount; ++i)
385    {
386      tr_torrent * tor = torrents[i];
387
388      if (tr_torrentCanManualUpdate (tor))
389        {
390          tr_torrentManualUpdate (tor);
391          notify (session, TR_RPC_TORRENT_CHANGED, tor);
392        }
393    }
394
395  tr_free (torrents);
396  return NULL;
397}
398
399static const char*
400torrentVerify (tr_session               * session,
401               tr_variant               * args_in,
402               tr_variant               * args_out UNUSED,
403               struct tr_rpc_idle_data  * idle_data UNUSED)
404{
405  int i;
406  int torrentCount;
407  tr_torrent ** torrents;
408
409  assert (idle_data == NULL);
410
411  torrents = getTorrents (session, args_in, &torrentCount);
412  for (i=0; i<torrentCount; ++i)
413    {
414      tr_torrent * tor = torrents[i];
415      tr_torrentVerify (tor, NULL, NULL);
416      notify (session, TR_RPC_TORRENT_CHANGED, tor);
417    }
418
419  tr_free (torrents);
420  return NULL;
421}
422
423/***
424****
425***/
426
427static void
428addFileStats (const tr_torrent * tor, tr_variant * list)
429{
430  tr_file_index_t i;
431  tr_file_index_t n;
432  const tr_info * info = tr_torrentInfo (tor);
433  tr_file_stat * files = tr_torrentFiles (tor, &n);
434
435  for (i=0; i<info->fileCount; ++i)
436    {
437      const tr_file * file = &info->files[i];
438      tr_variant * d = tr_variantListAddDict (list, 3);
439      tr_variantDictAddInt (d, TR_KEY_bytesCompleted, files[i].bytesCompleted);
440      tr_variantDictAddInt (d, TR_KEY_priority, file->priority);
441      tr_variantDictAddBool (d, TR_KEY_wanted, !file->dnd);
442    }
443
444  tr_torrentFilesFree (files, n);
445}
446
447static void
448addFiles (const tr_torrent * tor, tr_variant * list)
449{
450  tr_file_index_t i;
451  tr_file_index_t n;
452  const tr_info * info = tr_torrentInfo (tor);
453  tr_file_stat *  files = tr_torrentFiles (tor, &n);
454
455  for (i=0; i<info->fileCount; ++i)
456    {
457      const tr_file * file = &info->files[i];
458      tr_variant * d = tr_variantListAddDict (list, 3);
459      tr_variantDictAddInt (d, TR_KEY_bytesCompleted, files[i].bytesCompleted);
460      tr_variantDictAddInt (d, TR_KEY_length, file->length);
461      tr_variantDictAddStr (d, TR_KEY_name, file->name);
462    }
463
464  tr_torrentFilesFree (files, n);
465}
466
467static void
468addWebseeds (const tr_info  * info,
469             tr_variant     * webseeds)
470{
471  unsigned int i;
472
473  for (i=0; i< info->webseedCount; ++i)
474    tr_variantListAddStr (webseeds, info->webseeds[i]);
475}
476
477static void
478addTrackers (const tr_info  * info,
479             tr_variant     * trackers)
480{
481  unsigned int i;
482
483  for (i=0; i<info->trackerCount; ++i)
484    {
485      const tr_tracker_info * t = &info->trackers[i];
486      tr_variant * d = tr_variantListAddDict (trackers, 4);
487      tr_variantDictAddStr (d, TR_KEY_announce, t->announce);
488      tr_variantDictAddInt (d, TR_KEY_id, t->id);
489      tr_variantDictAddStr (d, TR_KEY_scrape, t->scrape);
490      tr_variantDictAddInt (d, TR_KEY_tier, t->tier);
491    }
492}
493
494static void
495addTrackerStats (const tr_tracker_stat * st, int n, tr_variant * list)
496{
497  int i;
498
499  for (i=0; i<n; ++i)
500    {
501      const tr_tracker_stat * s = &st[i];
502      tr_variant * d = tr_variantListAddDict (list, 26);
503      tr_variantDictAddStr  (d, TR_KEY_announce, s->announce);
504      tr_variantDictAddInt  (d, TR_KEY_announceState, s->announceState);
505      tr_variantDictAddInt  (d, TR_KEY_downloadCount, s->downloadCount);
506      tr_variantDictAddBool (d, TR_KEY_hasAnnounced, s->hasAnnounced);
507      tr_variantDictAddBool (d, TR_KEY_hasScraped, s->hasScraped);
508      tr_variantDictAddStr  (d, TR_KEY_host, s->host);
509      tr_variantDictAddInt  (d, TR_KEY_id, s->id);
510      tr_variantDictAddBool (d, TR_KEY_isBackup, s->isBackup);
511      tr_variantDictAddInt  (d, TR_KEY_lastAnnouncePeerCount, s->lastAnnouncePeerCount);
512      tr_variantDictAddStr  (d, TR_KEY_lastAnnounceResult, s->lastAnnounceResult);
513      tr_variantDictAddInt  (d, TR_KEY_lastAnnounceStartTime, s->lastAnnounceStartTime);
514      tr_variantDictAddBool (d, TR_KEY_lastAnnounceSucceeded, s->lastAnnounceSucceeded);
515      tr_variantDictAddInt  (d, TR_KEY_lastAnnounceTime, s->lastAnnounceTime);
516      tr_variantDictAddBool (d, TR_KEY_lastAnnounceTimedOut, s->lastAnnounceTimedOut);
517      tr_variantDictAddStr  (d, TR_KEY_lastScrapeResult, s->lastScrapeResult);
518      tr_variantDictAddInt  (d, TR_KEY_lastScrapeStartTime, s->lastScrapeStartTime);
519      tr_variantDictAddBool (d, TR_KEY_lastScrapeSucceeded, s->lastScrapeSucceeded);
520      tr_variantDictAddInt  (d, TR_KEY_lastScrapeTime, s->lastScrapeTime);
521      tr_variantDictAddInt  (d, TR_KEY_lastScrapeTimedOut, s->lastScrapeTimedOut);
522      tr_variantDictAddInt  (d, TR_KEY_leecherCount, s->leecherCount);
523      tr_variantDictAddInt  (d, TR_KEY_nextAnnounceTime, s->nextAnnounceTime);
524      tr_variantDictAddInt  (d, TR_KEY_nextScrapeTime, s->nextScrapeTime);
525      tr_variantDictAddStr  (d, TR_KEY_scrape, s->scrape);
526      tr_variantDictAddInt  (d, TR_KEY_scrapeState, s->scrapeState);
527      tr_variantDictAddInt  (d, TR_KEY_seederCount, s->seederCount);
528      tr_variantDictAddInt  (d, TR_KEY_tier, s->tier);
529    }
530}
531
532static void
533addPeers (tr_torrent * tor, tr_variant * list)
534{
535  int i;
536  int peerCount;
537  tr_peer_stat * peers = tr_torrentPeers (tor, &peerCount);
538
539  tr_variantInitList (list, peerCount);
540
541  for (i=0; i<peerCount; ++i)
542    {
543      tr_variant * d = tr_variantListAddDict (list, 16);
544      const tr_peer_stat * peer = peers + i;
545      tr_variantDictAddStr  (d, TR_KEY_address, peer->addr);
546      tr_variantDictAddStr  (d, TR_KEY_clientName, peer->client);
547      tr_variantDictAddBool (d, TR_KEY_clientIsChoked, peer->clientIsChoked);
548      tr_variantDictAddBool (d, TR_KEY_clientIsInterested, peer->clientIsInterested);
549      tr_variantDictAddStr  (d, TR_KEY_flagStr, peer->flagStr);
550      tr_variantDictAddBool (d, TR_KEY_isDownloadingFrom, peer->isDownloadingFrom);
551      tr_variantDictAddBool (d, TR_KEY_isEncrypted, peer->isEncrypted);
552      tr_variantDictAddBool (d, TR_KEY_isIncoming, peer->isIncoming);
553      tr_variantDictAddBool (d, TR_KEY_isUploadingTo, peer->isUploadingTo);
554      tr_variantDictAddBool (d, TR_KEY_isUTP, peer->isUTP);
555      tr_variantDictAddBool (d, TR_KEY_peerIsChoked, peer->peerIsChoked);
556      tr_variantDictAddBool (d, TR_KEY_peerIsInterested, peer->peerIsInterested);
557      tr_variantDictAddInt  (d, TR_KEY_port, peer->port);
558      tr_variantDictAddReal (d, TR_KEY_progress, peer->progress);
559      tr_variantDictAddInt  (d, TR_KEY_rateToClient, toSpeedBytes (peer->rateToClient_KBps));
560      tr_variantDictAddInt  (d, TR_KEY_rateToPeer, toSpeedBytes (peer->rateToPeer_KBps));
561    }
562
563  tr_torrentPeersFree (peers, peerCount);
564}
565
566static void
567addField (tr_torrent       * const tor,
568          const tr_info    * const inf,
569          const tr_stat    * const st,
570          tr_variant       * const d,
571          const tr_quark           key)
572{
573  char * str;
574
575  switch (key)
576    {
577      case TR_KEY_activityDate:
578        tr_variantDictAddInt (d, key, st->activityDate);
579        break;
580
581      case TR_KEY_addedDate:
582        tr_variantDictAddInt (d, key, st->addedDate);
583        break;
584
585      case TR_KEY_bandwidthPriority:
586        tr_variantDictAddInt (d, key, tr_torrentGetPriority (tor));
587        break;
588
589      case TR_KEY_comment:
590        tr_variantDictAddStr (d, key, inf->comment ? inf->comment : "");
591        break;
592
593      case TR_KEY_corruptEver:
594        tr_variantDictAddInt (d, key, st->corruptEver);
595        break;
596
597      case TR_KEY_creator:
598        tr_variantDictAddStr (d, key, inf->creator ? inf->creator : "");
599        break;
600
601      case TR_KEY_dateCreated:
602        tr_variantDictAddInt (d, key, inf->dateCreated);
603        break;
604
605      case TR_KEY_desiredAvailable:
606        tr_variantDictAddInt (d, key, st->desiredAvailable);
607        break;
608
609      case TR_KEY_doneDate:
610        tr_variantDictAddInt (d, key, st->doneDate);
611        break;
612
613      case TR_KEY_downloadDir:
614        tr_variantDictAddStr (d, key, tr_torrentGetDownloadDir (tor));
615        break;
616
617      case TR_KEY_downloadedEver:
618        tr_variantDictAddInt (d, key, st->downloadedEver);
619        break;
620
621      case TR_KEY_downloadLimit:
622        tr_variantDictAddInt (d, key, tr_torrentGetSpeedLimit_KBps (tor, TR_DOWN));
623        break;
624
625      case TR_KEY_downloadLimited:
626        tr_variantDictAddBool (d, key, tr_torrentUsesSpeedLimit (tor, TR_DOWN));
627        break;
628
629      case TR_KEY_error:
630        tr_variantDictAddInt (d, key, st->error);
631        break;
632
633      case TR_KEY_errorString:
634        tr_variantDictAddStr (d, key, st->errorString);
635        break;
636
637      case TR_KEY_eta:
638        tr_variantDictAddInt (d, key, st->eta);
639        break;
640
641      case TR_KEY_files:
642        addFiles (tor, tr_variantDictAddList (d, key, inf->fileCount));
643        break;
644
645      case TR_KEY_fileStats:
646        addFileStats (tor, tr_variantDictAddList (d, key, inf->fileCount));
647        break;
648
649      case TR_KEY_hashString:
650        tr_variantDictAddStr (d, key, tor->info.hashString);
651        break;
652
653      case TR_KEY_haveUnchecked:
654        tr_variantDictAddInt (d, key, st->haveUnchecked);
655        break;
656
657      case TR_KEY_haveValid:
658        tr_variantDictAddInt (d, key, st->haveValid);
659        break;
660
661      case TR_KEY_honorsSessionLimits:
662        tr_variantDictAddBool (d, key, tr_torrentUsesSessionLimits (tor));
663        break;
664
665      case TR_KEY_id:
666        tr_variantDictAddInt (d, key, st->id);
667        break;
668
669      case TR_KEY_isFinished:
670        tr_variantDictAddBool (d, key, st->finished);
671        break;
672
673      case TR_KEY_isPrivate:
674        tr_variantDictAddBool (d, key, tr_torrentIsPrivate (tor));
675        break;
676
677      case TR_KEY_isStalled:
678        tr_variantDictAddBool (d, key, st->isStalled);
679        break;
680
681      case TR_KEY_leftUntilDone:
682        tr_variantDictAddInt (d, key, st->leftUntilDone);
683        break;
684
685      case TR_KEY_manualAnnounceTime:
686        tr_variantDictAddInt (d, key, st->manualAnnounceTime);
687        break;
688
689      case TR_KEY_maxConnectedPeers:
690        tr_variantDictAddInt (d, key,  tr_torrentGetPeerLimit (tor));
691        break;
692
693      case TR_KEY_magnetLink:
694        str = tr_torrentGetMagnetLink (tor);
695        tr_variantDictAddStr (d, key, str);
696        tr_free (str);
697        break;
698
699      case TR_KEY_metadataPercentComplete:
700        tr_variantDictAddReal (d, key, st->metadataPercentComplete);
701        break;
702
703      case TR_KEY_name:
704        tr_variantDictAddStr (d, key, tr_torrentName (tor));
705        break;
706
707      case TR_KEY_percentDone:
708        tr_variantDictAddReal (d, key, st->percentDone);
709        break;
710
711      case TR_KEY_peer_limit:
712        tr_variantDictAddInt (d, key, tr_torrentGetPeerLimit (tor));
713        break;
714
715      case TR_KEY_peers:
716        addPeers (tor, tr_variantDictAdd (d, key));
717        break;
718
719      case TR_KEY_peersConnected:
720        tr_variantDictAddInt (d, key, st->peersConnected);
721        break;
722
723      case TR_KEY_peersFrom:
724        {
725          tr_variant * tmp = tr_variantDictAddDict (d, key, 7);
726          const int * f = st->peersFrom;
727          tr_variantDictAddInt (tmp, TR_KEY_fromCache,    f[TR_PEER_FROM_RESUME]);
728          tr_variantDictAddInt (tmp, TR_KEY_fromDht,      f[TR_PEER_FROM_DHT]);
729          tr_variantDictAddInt (tmp, TR_KEY_fromIncoming, f[TR_PEER_FROM_INCOMING]);
730          tr_variantDictAddInt (tmp, TR_KEY_fromLpd,      f[TR_PEER_FROM_LPD]);
731          tr_variantDictAddInt (tmp, TR_KEY_fromLtep,     f[TR_PEER_FROM_LTEP]);
732          tr_variantDictAddInt (tmp, TR_KEY_fromPex,      f[TR_PEER_FROM_PEX]);
733          tr_variantDictAddInt (tmp, TR_KEY_fromTracker,  f[TR_PEER_FROM_TRACKER]);
734          break;
735        }
736
737      case TR_KEY_peersGettingFromUs:
738        tr_variantDictAddInt (d, key, st->peersGettingFromUs);
739        break;
740
741      case TR_KEY_peersSendingToUs:
742        tr_variantDictAddInt (d, key, st->peersSendingToUs);
743        break;
744
745      case TR_KEY_pieces:
746        if (tr_torrentHasMetadata (tor))
747          {
748            size_t byte_count = 0;
749            void * bytes = tr_torrentCreatePieceBitfield (tor, &byte_count);
750            char * str = tr_base64_encode (bytes, byte_count, NULL);
751            tr_variantDictAddStr (d, key, str!=NULL ? str : "");
752            tr_free (str);
753            tr_free (bytes);
754          }
755        else
756          {
757            tr_variantDictAddStr (d, key, "");
758          }
759        break;
760
761      case TR_KEY_pieceCount:
762        tr_variantDictAddInt (d, key, inf->pieceCount);
763        break;
764
765      case TR_KEY_pieceSize:
766        tr_variantDictAddInt (d, key, inf->pieceSize);
767        break;
768
769      case TR_KEY_priorities:
770        {
771          tr_file_index_t i;
772          tr_variant * p = tr_variantDictAddList (d, key, inf->fileCount);
773          for (i=0; i<inf->fileCount; ++i)
774            tr_variantListAddInt (p, inf->files[i].priority);
775          break;
776        }
777
778      case TR_KEY_queuePosition:
779        tr_variantDictAddInt (d, key, st->queuePosition);
780        break;
781
782      case TR_KEY_etaIdle:
783        tr_variantDictAddInt (d, key, st->etaIdle);
784        break;
785
786      case TR_KEY_rateDownload:
787        tr_variantDictAddInt (d, key, toSpeedBytes (st->pieceDownloadSpeed_KBps));
788        break;
789
790      case TR_KEY_rateUpload:
791        tr_variantDictAddInt (d, key, toSpeedBytes (st->pieceUploadSpeed_KBps));
792        break;
793
794      case TR_KEY_recheckProgress:
795        tr_variantDictAddReal (d, key, st->recheckProgress);
796        break;
797
798      case TR_KEY_seedIdleLimit:
799        tr_variantDictAddInt (d, key, tr_torrentGetIdleLimit (tor));
800        break;
801
802      case TR_KEY_seedIdleMode:
803        tr_variantDictAddInt (d, key, tr_torrentGetIdleMode (tor));
804        break;
805
806      case TR_KEY_seedRatioLimit:
807        tr_variantDictAddReal (d, key, tr_torrentGetRatioLimit (tor));
808        break;
809
810      case TR_KEY_seedRatioMode:
811        tr_variantDictAddInt (d, key, tr_torrentGetRatioMode (tor));
812        break;
813
814      case TR_KEY_sizeWhenDone:
815        tr_variantDictAddInt (d, key, st->sizeWhenDone);
816        break;
817
818      case TR_KEY_startDate:
819        tr_variantDictAddInt (d, key, st->startDate);
820        break;
821
822      case TR_KEY_status:
823        tr_variantDictAddInt (d, key, st->activity);
824        break;
825
826      case TR_KEY_secondsDownloading:
827        tr_variantDictAddInt (d, key, st->secondsDownloading);
828        break;
829
830      case TR_KEY_secondsSeeding:
831        tr_variantDictAddInt (d, key, st->secondsSeeding);
832        break;
833
834      case TR_KEY_trackers:
835        addTrackers (inf, tr_variantDictAddList (d, key, inf->trackerCount));
836        break;
837
838      case TR_KEY_trackerStats:
839        {
840          int n;
841          tr_tracker_stat * s = tr_torrentTrackers (tor, &n);
842          addTrackerStats (s, n, tr_variantDictAddList (d, key, n));
843          tr_torrentTrackersFree (s, n);
844          break;
845        }
846
847      case TR_KEY_torrentFile:
848        tr_variantDictAddStr (d, key, inf->torrent);
849        break;
850
851      case TR_KEY_totalSize:
852        tr_variantDictAddInt (d, key, inf->totalSize);
853        break;
854
855      case TR_KEY_uploadedEver:
856        tr_variantDictAddInt (d, key, st->uploadedEver);
857        break;
858
859      case TR_KEY_uploadLimit:
860        tr_variantDictAddInt (d, key, tr_torrentGetSpeedLimit_KBps (tor, TR_UP));
861        break;
862
863      case TR_KEY_uploadLimited:
864        tr_variantDictAddBool (d, key, tr_torrentUsesSpeedLimit (tor, TR_UP));
865        break;
866
867      case TR_KEY_uploadRatio:
868        tr_variantDictAddReal (d, key, st->ratio);
869        break;
870
871      case TR_KEY_wanted:
872        {
873          tr_file_index_t i;
874          tr_variant * w = tr_variantDictAddList (d, key, inf->fileCount);
875          for (i=0; i<inf->fileCount; ++i)
876            tr_variantListAddInt (w, inf->files[i].dnd ? 0 : 1);
877          break;
878        }
879
880      case TR_KEY_webseeds:
881        addWebseeds (inf, tr_variantDictAddList (d, key, inf->webseedCount));
882        break;
883
884      case TR_KEY_webseedsSendingToUs:
885        tr_variantDictAddInt (d, key, st->webseedsSendingToUs);
886        break;
887
888      default:
889        break;
890    }
891}
892
893static void
894addInfo (tr_torrent * tor, tr_variant * d, tr_variant * fields)
895{
896  const int n = tr_variantListSize (fields);
897
898  tr_variantInitDict (d, n);
899
900  if (n > 0)
901    {
902      int i;
903      const tr_info * const inf = tr_torrentInfo (tor);
904      const tr_stat * const st = tr_torrentStat ((tr_torrent*)tor);
905
906      for (i=0; i<n; ++i)
907        {
908          size_t len;
909          const char * str;
910          if (tr_variantGetStr (tr_variantListChild (fields, i), &str, &len))
911            addField (tor, inf, st, d, tr_quark_new (str, len));
912        }
913    }
914}
915
916static const char*
917torrentGet (tr_session               * session,
918            tr_variant               * args_in,
919            tr_variant               * args_out,
920            struct tr_rpc_idle_data  * idle_data UNUSED)
921{
922  int i;
923  int torrentCount;
924  tr_torrent ** torrents = getTorrents (session, args_in, &torrentCount);
925  tr_variant * list = tr_variantDictAddList (args_out, TR_KEY_torrents, torrentCount);
926  tr_variant * fields;
927  const char * strVal;
928  const char * errmsg = NULL;
929
930  assert (idle_data == NULL);
931
932  if (tr_variantDictFindStr (args_in, TR_KEY_ids, &strVal, NULL) && !strcmp (strVal, "recently-active"))
933    {
934      int n = 0;
935      tr_variant * d;
936      const time_t now = tr_time ();
937      const int interval = RECENTLY_ACTIVE_SECONDS;
938      tr_variant * removed_out = tr_variantDictAddList (args_out, TR_KEY_removed, 0);
939      while ((d = tr_variantListChild (&session->removedTorrents, n++)))
940        {
941          int64_t intVal;
942          if (tr_variantDictFindInt (d, TR_KEY_date, &intVal) && (intVal >= now - interval))
943            {
944              tr_variantDictFindInt (d, TR_KEY_id, &intVal);
945              tr_variantListAddInt (removed_out, intVal);
946            }
947        }
948    }
949
950  if (!tr_variantDictFindList (args_in, TR_KEY_fields, &fields))
951    errmsg = "no fields specified";
952  else for (i=0; i<torrentCount; ++i)
953    addInfo (torrents[i], tr_variantListAdd (list), fields);
954
955  tr_free (torrents);
956  return errmsg;
957}
958
959/***
960****
961***/
962
963static const char*
964setFilePriorities (tr_torrent * tor,
965                   int          priority,
966                   tr_variant * list)
967{
968  int i;
969  int64_t tmp;
970  int fileCount = 0;
971  const int n = tr_variantListSize (list);
972  const char * errmsg = NULL;
973  tr_file_index_t * files = tr_new0 (tr_file_index_t, tor->info.fileCount);
974
975  if (n)
976    {
977      for (i=0; i<n; ++i)
978        {
979          if (tr_variantGetInt (tr_variantListChild (list, i), &tmp))
980            {
981              if (0 <= tmp && tmp < tor->info.fileCount)
982                files[fileCount++] = tmp;
983              else
984                errmsg = "file index out of range";
985            }
986        }
987    }
988  else /* if empty set, apply to all */
989    {
990      tr_file_index_t t;
991      for (t=0; t<tor->info.fileCount; ++t)
992        files[fileCount++] = t;
993    }
994
995  if (fileCount)
996    tr_torrentSetFilePriorities (tor, files, fileCount, priority);
997
998  tr_free (files);
999  return errmsg;
1000}
1001
1002static const char*
1003setFileDLs (tr_torrent * tor,
1004            int          do_download,
1005            tr_variant * list)
1006{
1007  int i;
1008  int64_t tmp;
1009  int fileCount = 0;
1010  const int n = tr_variantListSize (list);
1011  const char * errmsg = NULL;
1012  tr_file_index_t * files = tr_new0 (tr_file_index_t, tor->info.fileCount);
1013
1014  if (n) /* if argument list, process them */
1015    {
1016      for (i=0; i<n; ++i)
1017        {
1018          if (tr_variantGetInt (tr_variantListChild (list, i), &tmp))
1019            {
1020              if (0 <= tmp && tmp < tor->info.fileCount)
1021                files[fileCount++] = tmp;
1022              else
1023                errmsg = "file index out of range";
1024            }
1025        }
1026    }
1027  else /* if empty set, apply to all */
1028    {
1029      tr_file_index_t t;
1030
1031      for (t=0; t<tor->info.fileCount; ++t)
1032        files[fileCount++] = t;
1033    }
1034
1035  if (fileCount)
1036      tr_torrentSetFileDLs (tor, files, fileCount, do_download);
1037
1038  tr_free (files);
1039  return errmsg;
1040}
1041
1042static bool
1043findAnnounceUrl (const tr_tracker_info * t, int n, const char * url, int * pos)
1044{
1045  int i;
1046  bool found = false;
1047
1048  for (i=0; i<n; ++i)
1049    {
1050      if (!strcmp (t[i].announce, url))
1051        {
1052          found = true;
1053
1054          if (pos != NULL)
1055            *pos = i;
1056
1057          break;
1058        }
1059    }
1060
1061  return found;
1062}
1063
1064static int
1065copyTrackers (tr_tracker_info * tgt, const tr_tracker_info * src, int n)
1066{
1067  int i;
1068  int maxTier = -1;
1069
1070  for (i=0; i<n; ++i)
1071    {
1072      tgt[i].tier = src[i].tier;
1073      tgt[i].announce = tr_strdup (src[i].announce);
1074      maxTier = MAX (maxTier, src[i].tier);
1075    }
1076
1077  return maxTier;
1078}
1079
1080static void
1081freeTrackers (tr_tracker_info * trackers, int n)
1082{
1083  int i;
1084
1085  for (i=0; i<n; ++i)
1086    tr_free (trackers[i].announce);
1087
1088  tr_free (trackers);
1089}
1090
1091static const char*
1092addTrackerUrls (tr_torrent * tor, tr_variant * urls)
1093{
1094  int i;
1095  int n;
1096  int tier;
1097  tr_variant * val;
1098  tr_tracker_info * trackers;
1099  bool changed = false;
1100  const tr_info * inf = tr_torrentInfo (tor);
1101  const char * errmsg = NULL;
1102
1103  /* make a working copy of the existing announce list */
1104  n = inf->trackerCount;
1105  trackers = tr_new0 (tr_tracker_info, n + tr_variantListSize (urls));
1106  tier = copyTrackers (trackers, inf->trackers, n);
1107
1108  /* and add the new ones */
1109  i = 0;
1110  while ((val = tr_variantListChild (urls, i++)))
1111    {
1112      const char * announce = NULL;
1113
1114      if ( tr_variantGetStr (val, &announce, NULL)
1115          && tr_urlIsValidTracker (announce)
1116          && !findAnnounceUrl (trackers, n, announce, NULL))
1117        {
1118          trackers[n].tier = ++tier; /* add a new tier */
1119          trackers[n].announce = tr_strdup (announce);
1120          ++n;
1121          changed = true;
1122        }
1123    }
1124
1125  if (!changed)
1126    errmsg = "invalid argument";
1127  else if (!tr_torrentSetAnnounceList (tor, trackers, n))
1128    errmsg = "error setting announce list";
1129
1130  freeTrackers (trackers, n);
1131  return errmsg;
1132}
1133
1134static const char*
1135replaceTrackers (tr_torrent * tor, tr_variant * urls)
1136{
1137  int i;
1138  tr_variant * pair[2];
1139  tr_tracker_info * trackers;
1140  bool changed = false;
1141  const tr_info * inf = tr_torrentInfo (tor);
1142  const int n = inf->trackerCount;
1143  const char * errmsg = NULL;
1144
1145  /* make a working copy of the existing announce list */
1146  trackers = tr_new0 (tr_tracker_info, n);
1147  copyTrackers (trackers, inf->trackers, n);
1148
1149  /* make the substitutions... */
1150  i = 0;
1151  while (((pair[0] = tr_variantListChild (urls,i))) &&
1152         ((pair[1] = tr_variantListChild (urls,i+1))))
1153    {
1154      size_t len;
1155      int64_t pos;
1156      const char * newval;
1157
1158      if (tr_variantGetInt (pair[0], &pos)
1159          && tr_variantGetStr (pair[1], &newval, &len)
1160          && tr_urlIsValidTracker (newval)
1161          && pos < n
1162          && pos >= 0)
1163        {
1164          tr_free (trackers[pos].announce);
1165          trackers[pos].announce = tr_strndup (newval, len);
1166          changed = true;
1167        }
1168
1169      i += 2;
1170    }
1171
1172  if (!changed)
1173    errmsg = "invalid argument";
1174  else if (!tr_torrentSetAnnounceList (tor, trackers, n))
1175    errmsg = "error setting announce list";
1176
1177  freeTrackers (trackers, n);
1178  return errmsg;
1179}
1180
1181static const char*
1182removeTrackers (tr_torrent * tor, tr_variant * ids)
1183{
1184  int i;
1185  int n;
1186  int t = 0;
1187  int dup = -1;
1188  int * tids;
1189  tr_variant * val;
1190  tr_tracker_info * trackers;
1191  bool changed = false;
1192  const tr_info * inf = tr_torrentInfo (tor);
1193  const char * errmsg = NULL;
1194
1195  /* make a working copy of the existing announce list */
1196  n = inf->trackerCount;
1197  tids = tr_new0 (int, n);
1198  trackers = tr_new0 (tr_tracker_info, n);
1199  copyTrackers (trackers, inf->trackers, n);
1200
1201  /* remove the ones specified in the urls list */
1202  i = 0;
1203  while ((val = tr_variantListChild (ids, i++)))
1204    {
1205      int64_t pos;
1206
1207      if (tr_variantGetInt (val, &pos) && (0 <= pos) && (pos < n))
1208        tids[t++] = pos;
1209    }
1210
1211  /* sort trackerIds and remove from largest to smallest so there is no need to recacluate array indicies */
1212  qsort (tids, t, sizeof (int), compareInt);
1213  while (t--)
1214    {
1215      /* check for duplicates */
1216      if (tids[t] == dup)
1217        continue;
1218      tr_removeElementFromArray (trackers, tids[t], sizeof (tr_tracker_info), n--);
1219      dup = tids[t];
1220      changed = true;
1221    }
1222
1223  if (!changed)
1224    errmsg = "invalid argument";
1225  else if (!tr_torrentSetAnnounceList (tor, trackers, n))
1226    errmsg = "error setting announce list";
1227
1228  freeTrackers (trackers, n);
1229  tr_free (tids);
1230  return errmsg;
1231}
1232
1233static const char*
1234torrentSet (tr_session               * session,
1235            tr_variant                  * args_in,
1236            tr_variant                  * args_out UNUSED,
1237            struct tr_rpc_idle_data  * idle_data UNUSED)
1238{
1239  int i;
1240  int torrentCount;
1241  tr_torrent ** torrents;
1242  const char * errmsg = NULL;
1243
1244  assert (idle_data == NULL);
1245
1246  torrents = getTorrents (session, args_in, &torrentCount);
1247
1248  for (i=0; i<torrentCount; ++i)
1249    {
1250      int64_t tmp;
1251      double d;
1252      tr_variant * files;
1253      tr_variant * trackers;
1254      bool boolVal;
1255      tr_torrent * tor;
1256
1257      tor = torrents[i];
1258
1259      if (tr_variantDictFindInt (args_in, TR_KEY_bandwidthPriority, &tmp))
1260        if (tr_isPriority (tmp))
1261          tr_torrentSetPriority (tor, tmp);
1262
1263      if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_files_unwanted, &files))
1264        errmsg = setFileDLs (tor, false, files);
1265
1266      if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_files_wanted, &files))
1267        errmsg = setFileDLs (tor, true, files);
1268
1269      if (tr_variantDictFindInt (args_in, TR_KEY_peer_limit, &tmp))
1270        tr_torrentSetPeerLimit (tor, tmp);
1271
1272      if (!errmsg &&  tr_variantDictFindList (args_in, TR_KEY_priority_high, &files))
1273        errmsg = setFilePriorities (tor, TR_PRI_HIGH, files);
1274
1275      if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_priority_low, &files))
1276        errmsg = setFilePriorities (tor, TR_PRI_LOW, files);
1277
1278      if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_priority_normal, &files))
1279        errmsg = setFilePriorities (tor, TR_PRI_NORMAL, files);
1280
1281      if (tr_variantDictFindInt (args_in, TR_KEY_downloadLimit, &tmp))
1282        tr_torrentSetSpeedLimit_KBps (tor, TR_DOWN, tmp);
1283
1284      if (tr_variantDictFindBool (args_in, TR_KEY_downloadLimited, &boolVal))
1285        tr_torrentUseSpeedLimit (tor, TR_DOWN, boolVal);
1286
1287      if (tr_variantDictFindBool (args_in, TR_KEY_honorsSessionLimits, &boolVal))
1288        tr_torrentUseSessionLimits (tor, boolVal);
1289
1290      if (tr_variantDictFindInt (args_in, TR_KEY_uploadLimit, &tmp))
1291        tr_torrentSetSpeedLimit_KBps (tor, TR_UP, tmp);
1292
1293      if (tr_variantDictFindBool (args_in, TR_KEY_uploadLimited, &boolVal))
1294        tr_torrentUseSpeedLimit (tor, TR_UP, boolVal);
1295
1296      if (tr_variantDictFindInt (args_in, TR_KEY_seedIdleLimit, &tmp))
1297        tr_torrentSetIdleLimit (tor, tmp);
1298
1299      if (tr_variantDictFindInt (args_in, TR_KEY_seedIdleMode, &tmp))
1300        tr_torrentSetIdleMode (tor, tmp);
1301
1302      if (tr_variantDictFindReal (args_in, TR_KEY_seedRatioLimit, &d))
1303        tr_torrentSetRatioLimit (tor, d);
1304
1305      if (tr_variantDictFindInt (args_in, TR_KEY_seedRatioMode, &tmp))
1306        tr_torrentSetRatioMode (tor, tmp);
1307
1308      if (tr_variantDictFindInt (args_in, TR_KEY_queuePosition, &tmp))
1309        tr_torrentSetQueuePosition (tor, tmp);
1310
1311      if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_trackerAdd, &trackers))
1312        errmsg = addTrackerUrls (tor, trackers);
1313
1314      if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_trackerRemove, &trackers))
1315        errmsg = removeTrackers (tor, trackers);
1316
1317      if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_trackerReplace, &trackers))
1318        errmsg = replaceTrackers (tor, trackers);
1319
1320      notify (session, TR_RPC_TORRENT_CHANGED, tor);
1321    }
1322
1323  tr_free (torrents);
1324  return errmsg;
1325}
1326
1327static const char*
1328torrentSetLocation (tr_session               * session,
1329                    tr_variant               * args_in,
1330                    tr_variant               * args_out UNUSED,
1331                    struct tr_rpc_idle_data  * idle_data UNUSED)
1332{
1333  const char * errmsg = NULL;
1334  const char * location = NULL;
1335
1336  assert (idle_data == NULL);
1337
1338  if (!tr_variantDictFindStr (args_in, TR_KEY_location, &location, NULL))
1339    {
1340      errmsg = "no location";
1341    }
1342  else
1343    {
1344      bool move = false;
1345      int i, torrentCount;
1346      tr_torrent ** torrents = getTorrents (session, args_in, &torrentCount);
1347
1348      tr_variantDictFindBool (args_in, TR_KEY_move, &move);
1349
1350      for (i=0; i<torrentCount; ++i)
1351        {
1352          tr_torrent * tor = torrents[i];
1353          tr_torrentSetLocation (tor, location, move, NULL, NULL);
1354          notify (session, TR_RPC_TORRENT_MOVED, tor);
1355        }
1356
1357      tr_free (torrents);
1358    }
1359
1360  return errmsg;
1361}
1362
1363/***
1364****
1365***/
1366
1367static void
1368torrentRenamePathDone (tr_torrent  * tor,
1369                       const char  * oldpath,
1370                       const char  * newname,
1371                       int           error,
1372                       void        * user_data)
1373{
1374  const char * result;
1375  struct tr_rpc_idle_data * data = user_data;
1376
1377  tr_variantDictAddInt (data->args_out, TR_KEY_id, tr_torrentId(tor));
1378  tr_variantDictAddStr (data->args_out, TR_KEY_path, oldpath);
1379  tr_variantDictAddStr (data->args_out, TR_KEY_name, newname);
1380
1381  if (error == 0)
1382    result = NULL;
1383  else
1384    result = tr_strerror (error);
1385
1386  tr_idle_function_done (data, result);
1387}
1388
1389static const char*
1390torrentRenamePath (tr_session               * session,
1391                   tr_variant               * args_in,
1392                   tr_variant               * args_out UNUSED,
1393                   struct tr_rpc_idle_data  * idle_data)
1394{
1395  int torrentCount;
1396  tr_torrent ** torrents;
1397  const char * oldpath = NULL;
1398  const char * newname = NULL;
1399
1400  tr_variantDictFindStr (args_in, TR_KEY_path, &oldpath, NULL);
1401  tr_variantDictFindStr (args_in, TR_KEY_name, &newname, NULL);
1402  torrents = getTorrents (session, args_in, &torrentCount);
1403
1404  if (torrentCount == 1)
1405    tr_torrentRenamePath (torrents[0], oldpath, newname, torrentRenamePathDone, idle_data);
1406  else
1407    tr_idle_function_done (idle_data, "torrent-rename-path requires 1 torrent");
1408
1409  /* cleanup */
1410  tr_free (torrents);
1411  return NULL; /* ignored */
1412}
1413
1414/***
1415****
1416***/
1417
1418static void
1419portTested (tr_session       * session UNUSED,
1420            bool               did_connect UNUSED,
1421            bool               did_timeout UNUSED,
1422            long               response_code,
1423            const void       * response,
1424            size_t             response_byte_count,
1425            void             * user_data)
1426{
1427  char result[1024];
1428  struct tr_rpc_idle_data * data = user_data;
1429
1430  if (response_code != 200)
1431    {
1432      tr_snprintf (result, sizeof (result), "portTested: http error %ld: %s",
1433                   response_code, tr_webGetResponseStr (response_code));
1434    }
1435  else /* success */
1436    {
1437      const bool isOpen = response_byte_count && * (char*)response == '1';
1438      tr_variantDictAddBool (data->args_out, TR_KEY_port_is_open, isOpen);
1439      tr_snprintf (result, sizeof (result), "success");
1440    }
1441
1442    tr_idle_function_done (data, result);
1443}
1444
1445static const char*
1446portTest (tr_session               * session,
1447          tr_variant               * args_in UNUSED,
1448          tr_variant               * args_out UNUSED,
1449          struct tr_rpc_idle_data  * idle_data)
1450{
1451  const int port = tr_sessionGetPeerPort (session);
1452  char * url = tr_strdup_printf ("http://portcheck.transmissionbt.com/%d", port);
1453  tr_webRun (session, url, portTested, idle_data);
1454  tr_free (url);
1455  return NULL;
1456}
1457
1458/***
1459****
1460***/
1461
1462static void
1463gotNewBlocklist (tr_session       * session,
1464                 bool               did_connect UNUSED,
1465                 bool               did_timeout UNUSED,
1466                 long               response_code,
1467                 const void       * response,
1468                 size_t             response_byte_count,
1469                 void             * user_data)
1470{
1471  char result[1024];
1472  struct tr_rpc_idle_data * data = user_data;
1473
1474  *result = '\0';
1475
1476  if (response_code != 200)
1477    {
1478      tr_snprintf (result, sizeof (result), "gotNewBlocklist: http error %ld: %s",
1479                   response_code, tr_webGetResponseStr (response_code));
1480    }
1481  else /* successfully fetched the blocklist... */
1482    {
1483      int fd;
1484      int err;
1485      char * filename;
1486      z_stream stream;
1487      const char * configDir = tr_sessionGetConfigDir (session);
1488      const size_t buflen = 1024 * 128; /* 128 KiB buffer */
1489      uint8_t * buf = tr_valloc (buflen);
1490
1491      /* this is an odd Magic Number required by zlib to enable gz support.
1492         See zlib's inflateInit2 () documentation for a full description */
1493      const int windowBits = 15 + 32;
1494
1495      stream.zalloc = (alloc_func) Z_NULL;
1496      stream.zfree = (free_func) Z_NULL;
1497      stream.opaque = (voidpf) Z_NULL;
1498      stream.next_in = (void*) response;
1499      stream.avail_in = response_byte_count;
1500      inflateInit2 (&stream, windowBits);
1501
1502      filename = tr_buildPath (configDir, "blocklist.tmp", NULL);
1503      fd = tr_open_file_for_writing (filename);
1504      if (fd < 0)
1505        tr_snprintf (result, sizeof (result), _("Couldn't save file \"%1$s\": %2$s"), filename, tr_strerror (errno));
1506
1507      for (;;)
1508        {
1509          stream.next_out = (void*) buf;
1510          stream.avail_out = buflen;
1511          err = inflate (&stream, Z_NO_FLUSH);
1512
1513          if (stream.avail_out < buflen)
1514            {
1515              const int e = write (fd, buf, buflen - stream.avail_out);
1516              if (e < 0)
1517                {
1518                  tr_snprintf (result, sizeof (result), _("Couldn't save file \"%1$s\": %2$s"), filename, tr_strerror (errno));
1519                  break;
1520                }
1521            }
1522
1523          if (err != Z_OK)
1524            {
1525              if ((err != Z_STREAM_END) && (err != Z_DATA_ERROR))
1526                tr_snprintf (result, sizeof (result), _("Error uncompressing blocklist: %s (%d)"), zError (err), err);
1527              break;
1528            }
1529        }
1530
1531      inflateEnd (&stream);
1532
1533      if (err == Z_DATA_ERROR) /* couldn't inflate it... it's probably already uncompressed */
1534        if (write (fd, response, response_byte_count) < 0)
1535          tr_snprintf (result, sizeof (result), _("Couldn't save file \"%1$s\": %2$s"), filename, tr_strerror (errno));
1536
1537      if (*result)
1538        {
1539          tr_logAddError ("%s", result);
1540        }
1541      else
1542        {
1543          /* feed it to the session and give the client a response */
1544          const int rule_count = tr_blocklistSetContent (session, filename);
1545          tr_variantDictAddInt (data->args_out, TR_KEY_blocklist_size, rule_count);
1546          tr_snprintf (result, sizeof (result), "success");
1547        }
1548
1549      tr_remove (filename);
1550      tr_free (filename);
1551      tr_free (buf);
1552    }
1553
1554  tr_idle_function_done (data, result);
1555}
1556
1557static const char*
1558blocklistUpdate (tr_session               * session,
1559                 tr_variant               * args_in UNUSED,
1560                 tr_variant               * args_out UNUSED,
1561                 struct tr_rpc_idle_data  * idle_data)
1562{
1563  tr_webRun (session, session->blocklist_url, gotNewBlocklist, idle_data);
1564  return NULL;
1565}
1566
1567/***
1568****
1569***/
1570
1571static void
1572addTorrentImpl (struct tr_rpc_idle_data * data, tr_ctor * ctor)
1573{
1574  int err;
1575  int duplicate_id;
1576  const char * result;
1577  tr_torrent * tor;
1578  tr_quark key;
1579
1580  err = 0;
1581  duplicate_id = 0;
1582  tor = tr_torrentNew (ctor, &err, &duplicate_id);
1583  tr_ctorFree (ctor);
1584
1585  if (!err)
1586    {
1587      key = TR_KEY_torrent_added;
1588      result = NULL;
1589    }
1590  else if (err == TR_PARSE_DUPLICATE)
1591    {
1592      tor = tr_torrentFindFromId (data->session, duplicate_id);
1593      key = TR_KEY_torrent_duplicate;
1594      result = "duplicate torrent";
1595    }
1596  else /* err == TR_PARSE_ERR */
1597    {
1598      key = 0;
1599      result = "invalid or corrupt torrent file";
1600    }
1601
1602  if (tor && key)
1603    {
1604      tr_variant fields;
1605      tr_variantInitList (&fields, 3);
1606      tr_variantListAddStr (&fields, "id");
1607      tr_variantListAddStr (&fields, "name");
1608      tr_variantListAddStr (&fields, "hashString");
1609      addInfo (tor, tr_variantDictAdd (data->args_out, key), &fields);
1610      notify (data->session, TR_RPC_TORRENT_ADDED, tor);
1611      tr_variantFree (&fields);
1612      result = NULL;
1613    }
1614
1615  tr_idle_function_done (data, result);
1616}
1617
1618
1619struct add_torrent_idle_data
1620{
1621  struct tr_rpc_idle_data * data;
1622  tr_ctor * ctor;
1623};
1624
1625static void
1626gotMetadataFromURL (tr_session       * session UNUSED,
1627                    bool               did_connect UNUSED,
1628                    bool               did_timeout UNUSED,
1629                    long               response_code,
1630                    const void       * response,
1631                    size_t             response_byte_count,
1632                    void             * user_data)
1633{
1634  struct add_torrent_idle_data * data = user_data;
1635
1636  dbgmsg ("torrentAdd: HTTP response code was %ld (%s); response length was %"TR_PRIuSIZE" bytes",
1637          response_code, tr_webGetResponseStr (response_code), response_byte_count);
1638
1639  if (response_code==200 || response_code==221) /* http or ftp success.. */
1640    {
1641      tr_ctorSetMetainfo (data->ctor, response, response_byte_count);
1642      addTorrentImpl (data->data, data->ctor);
1643    }
1644  else
1645    {
1646      char result[1024];
1647      tr_snprintf (result, sizeof (result), "gotMetadataFromURL: http error %ld: %s",
1648                   response_code, tr_webGetResponseStr (response_code));
1649      tr_idle_function_done (data->data, result);
1650    }
1651
1652  tr_free (data);
1653}
1654
1655static bool
1656isCurlURL (const char * filename)
1657{
1658  if (filename == NULL)
1659    return false;
1660
1661  return !strncmp (filename, "ftp://", 6) ||
1662         !strncmp (filename, "http://", 7) ||
1663         !strncmp (filename, "https://", 8);
1664}
1665
1666static tr_file_index_t*
1667fileListFromList (tr_variant * list, tr_file_index_t * setmeCount)
1668{
1669  size_t i;
1670  const size_t childCount = tr_variantListSize (list);
1671  tr_file_index_t n = 0;
1672  tr_file_index_t * files = tr_new0 (tr_file_index_t, childCount);
1673
1674  for (i=0; i<childCount; ++i)
1675    {
1676      int64_t intVal;
1677      if (tr_variantGetInt (tr_variantListChild (list, i), &intVal))
1678        files[n++] = (tr_file_index_t)intVal;
1679    }
1680
1681  *setmeCount = n;
1682  return files;
1683}
1684
1685static const char*
1686torrentAdd (tr_session               * session,
1687            tr_variant               * args_in,
1688            tr_variant               * args_out UNUSED,
1689            struct tr_rpc_idle_data  * idle_data)
1690{
1691  const char * filename = NULL;
1692  const char * metainfo_base64 = NULL;
1693
1694  assert (idle_data != NULL);
1695
1696  tr_variantDictFindStr (args_in, TR_KEY_filename, &filename, NULL);
1697  tr_variantDictFindStr (args_in, TR_KEY_metainfo, &metainfo_base64, NULL);
1698  if (!filename && !metainfo_base64)
1699    {
1700      return "no filename or metainfo specified";
1701    }
1702  else
1703    {
1704      int64_t i;
1705      bool boolVal;
1706      tr_variant * l;
1707      const char * str;
1708      const char * cookies = NULL;
1709      tr_ctor * ctor = tr_ctorNew (session);
1710
1711      /* set the optional arguments */
1712
1713      tr_variantDictFindStr (args_in, TR_KEY_cookies, &cookies, NULL);
1714
1715      if (tr_variantDictFindStr (args_in, TR_KEY_download_dir, &str, NULL))
1716        tr_ctorSetDownloadDir (ctor, TR_FORCE, str);
1717
1718      if (tr_variantDictFindBool (args_in, TR_KEY_paused, &boolVal))
1719        tr_ctorSetPaused (ctor, TR_FORCE, boolVal);
1720
1721      if (tr_variantDictFindInt (args_in, TR_KEY_peer_limit, &i))
1722        tr_ctorSetPeerLimit (ctor, TR_FORCE, i);
1723
1724      if (tr_variantDictFindInt (args_in, TR_KEY_bandwidthPriority, &i))
1725        tr_ctorSetBandwidthPriority (ctor, i);
1726
1727      if (tr_variantDictFindList (args_in, TR_KEY_files_unwanted, &l))
1728        {
1729          tr_file_index_t fileCount;
1730          tr_file_index_t * files = fileListFromList (l, &fileCount);
1731          tr_ctorSetFilesWanted (ctor, files, fileCount, false);
1732          tr_free (files);
1733        }
1734
1735      if (tr_variantDictFindList (args_in, TR_KEY_files_wanted, &l))
1736        {
1737          tr_file_index_t fileCount;
1738          tr_file_index_t * files = fileListFromList (l, &fileCount);
1739          tr_ctorSetFilesWanted (ctor, files, fileCount, true);
1740          tr_free (files);
1741        }
1742
1743      if (tr_variantDictFindList (args_in, TR_KEY_priority_low, &l))
1744        {
1745          tr_file_index_t fileCount;
1746          tr_file_index_t * files = fileListFromList (l, &fileCount);
1747          tr_ctorSetFilePriorities (ctor, files, fileCount, TR_PRI_LOW);
1748          tr_free (files);
1749        }
1750
1751      if (tr_variantDictFindList (args_in, TR_KEY_priority_normal, &l))
1752        {
1753          tr_file_index_t fileCount;
1754          tr_file_index_t * files = fileListFromList (l, &fileCount);
1755          tr_ctorSetFilePriorities (ctor, files, fileCount, TR_PRI_NORMAL);
1756          tr_free (files);
1757        }
1758
1759      if (tr_variantDictFindList (args_in, TR_KEY_priority_high, &l))
1760        {
1761          tr_file_index_t fileCount;
1762          tr_file_index_t * files = fileListFromList (l, &fileCount);
1763          tr_ctorSetFilePriorities (ctor, files, fileCount, TR_PRI_HIGH);
1764          tr_free (files);
1765        }
1766
1767      dbgmsg ("torrentAdd: filename is \"%s\"", filename ? filename : " (null)");
1768
1769      if (isCurlURL (filename))
1770        {
1771          struct add_torrent_idle_data * d = tr_new0 (struct add_torrent_idle_data, 1);
1772          d->data = idle_data;
1773          d->ctor = ctor;
1774          tr_webRunWithCookies (session, filename, cookies, gotMetadataFromURL, d);
1775        }
1776      else
1777        {
1778          char * fname = tr_strstrip (tr_strdup (filename));
1779
1780          if (fname == NULL)
1781            {
1782              int len;
1783              char * metainfo = tr_base64_decode (metainfo_base64, -1, &len);
1784              tr_ctorSetMetainfo (ctor, (uint8_t*)metainfo, len);
1785              tr_free (metainfo);
1786            }
1787          else if (!strncmp (fname, "magnet:?", 8))
1788            {
1789              tr_ctorSetMetainfoFromMagnetLink (ctor, fname);
1790            }
1791          else
1792            {
1793              tr_ctorSetMetainfoFromFile (ctor, fname);
1794            }
1795
1796          addTorrentImpl (idle_data, ctor);
1797
1798          tr_free (fname);
1799        }
1800
1801    }
1802
1803  return NULL;
1804}
1805
1806/***
1807****
1808***/
1809
1810static const char*
1811sessionSet (tr_session               * session,
1812            tr_variant               * args_in,
1813            tr_variant               * args_out UNUSED,
1814            struct tr_rpc_idle_data  * idle_data UNUSED)
1815{
1816  int64_t i;
1817  double d;
1818  bool boolVal;
1819  const char * str;
1820
1821  assert (idle_data == NULL);
1822
1823  if (tr_variantDictFindInt (args_in, TR_KEY_cache_size_mb, &i))
1824    tr_sessionSetCacheLimit_MB (session, i);
1825
1826  if (tr_variantDictFindInt (args_in, TR_KEY_alt_speed_up, &i))
1827    tr_sessionSetAltSpeed_KBps (session, TR_UP, i);
1828
1829  if (tr_variantDictFindInt (args_in, TR_KEY_alt_speed_down, &i))
1830    tr_sessionSetAltSpeed_KBps (session, TR_DOWN, i);
1831
1832  if (tr_variantDictFindBool (args_in, TR_KEY_alt_speed_enabled, &boolVal))
1833    tr_sessionUseAltSpeed (session, boolVal);
1834
1835  if (tr_variantDictFindInt (args_in, TR_KEY_alt_speed_time_begin, &i))
1836    tr_sessionSetAltSpeedBegin (session, i);
1837
1838  if (tr_variantDictFindInt (args_in, TR_KEY_alt_speed_time_end, &i))
1839    tr_sessionSetAltSpeedEnd (session, i);
1840
1841  if (tr_variantDictFindInt (args_in, TR_KEY_alt_speed_time_day, &i))
1842    tr_sessionSetAltSpeedDay (session, i);
1843
1844  if (tr_variantDictFindBool (args_in, TR_KEY_alt_speed_time_enabled, &boolVal))
1845    tr_sessionUseAltSpeedTime (session, boolVal);
1846
1847  if (tr_variantDictFindBool (args_in, TR_KEY_blocklist_enabled, &boolVal))
1848    tr_blocklistSetEnabled (session, boolVal);
1849
1850  if (tr_variantDictFindStr (args_in, TR_KEY_blocklist_url, &str, NULL))
1851    tr_blocklistSetURL (session, str);
1852
1853  if (tr_variantDictFindStr (args_in, TR_KEY_download_dir, &str, NULL))
1854    tr_sessionSetDownloadDir (session, str);
1855
1856  if (tr_variantDictFindInt (args_in, TR_KEY_queue_stalled_minutes, &i))
1857    tr_sessionSetQueueStalledMinutes (session, i);
1858
1859  if (tr_variantDictFindBool (args_in, TR_KEY_queue_stalled_enabled, &boolVal))
1860    tr_sessionSetQueueStalledEnabled (session, boolVal);
1861
1862  if (tr_variantDictFindInt (args_in, TR_KEY_download_queue_size, &i))
1863    tr_sessionSetQueueSize (session, TR_DOWN, i);
1864
1865  if (tr_variantDictFindBool (args_in, TR_KEY_download_queue_enabled, &boolVal))
1866    tr_sessionSetQueueEnabled (session, TR_DOWN, boolVal);
1867
1868  if (tr_variantDictFindStr (args_in, TR_KEY_incomplete_dir, &str, NULL))
1869    tr_sessionSetIncompleteDir (session, str);
1870
1871  if (tr_variantDictFindBool (args_in, TR_KEY_incomplete_dir_enabled, &boolVal))
1872    tr_sessionSetIncompleteDirEnabled (session, boolVal);
1873
1874  if (tr_variantDictFindInt (args_in, TR_KEY_peer_limit_global, &i))
1875    tr_sessionSetPeerLimit (session, i);
1876
1877  if (tr_variantDictFindInt (args_in, TR_KEY_peer_limit_per_torrent, &i))
1878    tr_sessionSetPeerLimitPerTorrent (session, i);
1879
1880  if (tr_variantDictFindBool (args_in, TR_KEY_pex_enabled, &boolVal))
1881    tr_sessionSetPexEnabled (session, boolVal);
1882
1883  if (tr_variantDictFindBool (args_in, TR_KEY_dht_enabled, &boolVal))
1884    tr_sessionSetDHTEnabled (session, boolVal);
1885
1886  if (tr_variantDictFindBool (args_in, TR_KEY_utp_enabled, &boolVal))
1887    tr_sessionSetUTPEnabled (session, boolVal);
1888
1889  if (tr_variantDictFindBool (args_in, TR_KEY_lpd_enabled, &boolVal))
1890    tr_sessionSetLPDEnabled (session, boolVal);
1891
1892  if (tr_variantDictFindBool (args_in, TR_KEY_peer_port_random_on_start, &boolVal))
1893    tr_sessionSetPeerPortRandomOnStart (session, boolVal);
1894
1895  if (tr_variantDictFindInt (args_in, TR_KEY_peer_port, &i))
1896    tr_sessionSetPeerPort (session, i);
1897
1898  if (tr_variantDictFindBool (args_in, TR_KEY_port_forwarding_enabled, &boolVal))
1899    tr_sessionSetPortForwardingEnabled (session, boolVal);
1900
1901  if (tr_variantDictFindBool (args_in, TR_KEY_rename_partial_files, &boolVal))
1902    tr_sessionSetIncompleteFileNamingEnabled (session, boolVal);
1903
1904  if (tr_variantDictFindReal (args_in, TR_KEY_seedRatioLimit, &d))
1905    tr_sessionSetRatioLimit (session, d);
1906
1907  if (tr_variantDictFindBool (args_in, TR_KEY_seedRatioLimited, &boolVal))
1908    tr_sessionSetRatioLimited (session, boolVal);
1909
1910  if (tr_variantDictFindInt (args_in, TR_KEY_idle_seeding_limit, &i))
1911    tr_sessionSetIdleLimit (session, i);
1912
1913  if (tr_variantDictFindBool (args_in, TR_KEY_idle_seeding_limit_enabled, &boolVal))
1914    tr_sessionSetIdleLimited (session, boolVal);
1915
1916  if (tr_variantDictFindBool (args_in, TR_KEY_start_added_torrents, &boolVal))
1917    tr_sessionSetPaused (session, !boolVal);
1918
1919  if (tr_variantDictFindBool (args_in, TR_KEY_seed_queue_enabled, &boolVal))
1920    tr_sessionSetQueueEnabled (session, TR_UP, boolVal);
1921
1922  if (tr_variantDictFindInt (args_in, TR_KEY_seed_queue_size, &i))
1923    tr_sessionSetQueueSize (session, TR_UP, i);
1924
1925  if (tr_variantDictFindStr (args_in, TR_KEY_script_torrent_done_filename, &str, NULL))
1926    tr_sessionSetTorrentDoneScript (session, str);
1927
1928  if (tr_variantDictFindBool (args_in, TR_KEY_script_torrent_done_enabled, &boolVal))
1929    tr_sessionSetTorrentDoneScriptEnabled (session, boolVal);
1930
1931  if (tr_variantDictFindBool (args_in, TR_KEY_trash_original_torrent_files, &boolVal))
1932    tr_sessionSetDeleteSource (session, boolVal);
1933
1934  if (tr_variantDictFindInt (args_in, TR_KEY_speed_limit_down, &i))
1935    tr_sessionSetSpeedLimit_KBps (session, TR_DOWN, i);
1936
1937  if (tr_variantDictFindBool (args_in, TR_KEY_speed_limit_down_enabled, &boolVal))
1938    tr_sessionLimitSpeed (session, TR_DOWN, boolVal);
1939
1940  if (tr_variantDictFindInt (args_in, TR_KEY_speed_limit_up, &i))
1941    tr_sessionSetSpeedLimit_KBps (session, TR_UP, i);
1942
1943  if (tr_variantDictFindBool (args_in, TR_KEY_speed_limit_up_enabled, &boolVal))
1944    tr_sessionLimitSpeed (session, TR_UP, boolVal);
1945
1946  if (tr_variantDictFindStr (args_in, TR_KEY_encryption, &str, NULL))
1947    {
1948      if (!tr_strcmp0 (str, "required"))
1949        tr_sessionSetEncryption (session, TR_ENCRYPTION_REQUIRED);
1950      else if (!tr_strcmp0 (str, "tolerated"))
1951        tr_sessionSetEncryption (session, TR_CLEAR_PREFERRED);
1952      else
1953        tr_sessionSetEncryption (session, TR_ENCRYPTION_PREFERRED);
1954    }
1955
1956  notify (session, TR_RPC_SESSION_CHANGED, NULL);
1957
1958  return NULL;
1959}
1960
1961static const char*
1962sessionStats (tr_session               * session,
1963              tr_variant               * args_in UNUSED,
1964              tr_variant               * args_out,
1965              struct tr_rpc_idle_data  * idle_data UNUSED)
1966{
1967  int running = 0;
1968  int total = 0;
1969  tr_variant * d;
1970  tr_session_stats currentStats = { 0.0f, 0, 0, 0, 0, 0 };
1971  tr_session_stats cumulativeStats = { 0.0f, 0, 0, 0, 0, 0 };
1972  tr_torrent * tor = NULL;
1973
1974  assert (idle_data == NULL);
1975
1976  while ((tor = tr_torrentNext (session, tor)))
1977    {
1978      ++total;
1979
1980      if (tor->isRunning)
1981        ++running;
1982    }
1983
1984  tr_sessionGetStats (session, &currentStats);
1985  tr_sessionGetCumulativeStats (session, &cumulativeStats);
1986
1987  tr_variantDictAddInt  (args_out, TR_KEY_activeTorrentCount, running);
1988  tr_variantDictAddReal (args_out, TR_KEY_downloadSpeed, tr_sessionGetPieceSpeed_Bps (session, TR_DOWN));
1989  tr_variantDictAddInt  (args_out, TR_KEY_pausedTorrentCount, total - running);
1990  tr_variantDictAddInt  (args_out, TR_KEY_torrentCount, total);
1991  tr_variantDictAddReal (args_out, TR_KEY_uploadSpeed, tr_sessionGetPieceSpeed_Bps (session, TR_UP));
1992
1993  d = tr_variantDictAddDict (args_out, TR_KEY_cumulative_stats, 5);
1994  tr_variantDictAddInt (d, TR_KEY_downloadedBytes, cumulativeStats.downloadedBytes);
1995  tr_variantDictAddInt (d, TR_KEY_filesAdded, cumulativeStats.filesAdded);
1996  tr_variantDictAddInt (d, TR_KEY_secondsActive, cumulativeStats.secondsActive);
1997  tr_variantDictAddInt (d, TR_KEY_sessionCount, cumulativeStats.sessionCount);
1998  tr_variantDictAddInt (d, TR_KEY_uploadedBytes, cumulativeStats.uploadedBytes);
1999
2000  d = tr_variantDictAddDict (args_out, TR_KEY_current_stats, 5);
2001  tr_variantDictAddInt (d, TR_KEY_downloadedBytes, currentStats.downloadedBytes);
2002  tr_variantDictAddInt (d, TR_KEY_filesAdded, currentStats.filesAdded);
2003  tr_variantDictAddInt (d, TR_KEY_secondsActive, currentStats.secondsActive);
2004  tr_variantDictAddInt (d, TR_KEY_sessionCount, currentStats.sessionCount);
2005  tr_variantDictAddInt (d, TR_KEY_uploadedBytes, currentStats.uploadedBytes);
2006
2007  return NULL;
2008}
2009
2010static const char*
2011sessionGet (tr_session               * s,
2012            tr_variant               * args_in UNUSED,
2013            tr_variant               * args_out,
2014            struct tr_rpc_idle_data  * idle_data UNUSED)
2015{
2016  const char * str;
2017  tr_variant * d = args_out;
2018
2019  assert (idle_data == NULL);
2020  tr_variantDictAddInt  (d, TR_KEY_alt_speed_up, tr_sessionGetAltSpeed_KBps (s,TR_UP));
2021  tr_variantDictAddInt  (d, TR_KEY_alt_speed_down, tr_sessionGetAltSpeed_KBps (s,TR_DOWN));
2022  tr_variantDictAddBool (d, TR_KEY_alt_speed_enabled, tr_sessionUsesAltSpeed (s));
2023  tr_variantDictAddInt  (d, TR_KEY_alt_speed_time_begin, tr_sessionGetAltSpeedBegin (s));
2024  tr_variantDictAddInt  (d, TR_KEY_alt_speed_time_end,tr_sessionGetAltSpeedEnd (s));
2025  tr_variantDictAddInt  (d, TR_KEY_alt_speed_time_day,tr_sessionGetAltSpeedDay (s));
2026  tr_variantDictAddBool (d, TR_KEY_alt_speed_time_enabled, tr_sessionUsesAltSpeedTime (s));
2027  tr_variantDictAddBool (d, TR_KEY_blocklist_enabled, tr_blocklistIsEnabled (s));
2028  tr_variantDictAddStr  (d, TR_KEY_blocklist_url, tr_blocklistGetURL (s));
2029  tr_variantDictAddInt  (d, TR_KEY_cache_size_mb, tr_sessionGetCacheLimit_MB (s));
2030  tr_variantDictAddInt  (d, TR_KEY_blocklist_size, tr_blocklistGetRuleCount (s));
2031  tr_variantDictAddStr  (d, TR_KEY_config_dir, tr_sessionGetConfigDir (s));
2032  tr_variantDictAddStr  (d, TR_KEY_download_dir, tr_sessionGetDownloadDir (s));
2033  tr_variantDictAddInt  (d, TR_KEY_download_dir_free_space, tr_device_info_get_free_space (s->downloadDir));
2034  tr_variantDictAddBool (d, TR_KEY_download_queue_enabled, tr_sessionGetQueueEnabled (s, TR_DOWN));
2035  tr_variantDictAddInt  (d, TR_KEY_download_queue_size, tr_sessionGetQueueSize (s, TR_DOWN));
2036  tr_variantDictAddInt  (d, TR_KEY_peer_limit_global, tr_sessionGetPeerLimit (s));
2037  tr_variantDictAddInt  (d, TR_KEY_peer_limit_per_torrent, tr_sessionGetPeerLimitPerTorrent (s));
2038  tr_variantDictAddStr  (d, TR_KEY_incomplete_dir, tr_sessionGetIncompleteDir (s));
2039  tr_variantDictAddBool (d, TR_KEY_incomplete_dir_enabled, tr_sessionIsIncompleteDirEnabled (s));
2040  tr_variantDictAddBool (d, TR_KEY_pex_enabled, tr_sessionIsPexEnabled (s));
2041  tr_variantDictAddBool (d, TR_KEY_utp_enabled, tr_sessionIsUTPEnabled (s));
2042  tr_variantDictAddBool (d, TR_KEY_dht_enabled, tr_sessionIsDHTEnabled (s));
2043  tr_variantDictAddBool (d, TR_KEY_lpd_enabled, tr_sessionIsLPDEnabled (s));
2044  tr_variantDictAddInt  (d, TR_KEY_peer_port, tr_sessionGetPeerPort (s));
2045  tr_variantDictAddBool (d, TR_KEY_peer_port_random_on_start, tr_sessionGetPeerPortRandomOnStart (s));
2046  tr_variantDictAddBool (d, TR_KEY_port_forwarding_enabled, tr_sessionIsPortForwardingEnabled (s));
2047  tr_variantDictAddBool (d, TR_KEY_rename_partial_files, tr_sessionIsIncompleteFileNamingEnabled (s));
2048  tr_variantDictAddInt  (d, TR_KEY_rpc_version, RPC_VERSION);
2049  tr_variantDictAddInt  (d, TR_KEY_rpc_version_minimum, RPC_VERSION_MIN);
2050  tr_variantDictAddReal (d, TR_KEY_seedRatioLimit, tr_sessionGetRatioLimit (s));
2051  tr_variantDictAddBool (d, TR_KEY_seedRatioLimited, tr_sessionIsRatioLimited (s));
2052  tr_variantDictAddInt  (d, TR_KEY_idle_seeding_limit, tr_sessionGetIdleLimit (s));
2053  tr_variantDictAddBool (d, TR_KEY_idle_seeding_limit_enabled, tr_sessionIsIdleLimited (s));
2054  tr_variantDictAddBool (d, TR_KEY_seed_queue_enabled, tr_sessionGetQueueEnabled (s, TR_UP));
2055  tr_variantDictAddInt  (d, TR_KEY_seed_queue_size, tr_sessionGetQueueSize (s, TR_UP));
2056  tr_variantDictAddBool (d, TR_KEY_start_added_torrents, !tr_sessionGetPaused (s));
2057  tr_variantDictAddBool (d, TR_KEY_trash_original_torrent_files, tr_sessionGetDeleteSource (s));
2058  tr_variantDictAddInt  (d, TR_KEY_speed_limit_up, tr_sessionGetSpeedLimit_KBps (s, TR_UP));
2059  tr_variantDictAddBool (d, TR_KEY_speed_limit_up_enabled, tr_sessionIsSpeedLimited (s, TR_UP));
2060  tr_variantDictAddInt  (d, TR_KEY_speed_limit_down, tr_sessionGetSpeedLimit_KBps (s, TR_DOWN));
2061  tr_variantDictAddBool (d, TR_KEY_speed_limit_down_enabled, tr_sessionIsSpeedLimited (s, TR_DOWN));
2062  tr_variantDictAddStr  (d, TR_KEY_script_torrent_done_filename, tr_sessionGetTorrentDoneScript (s));
2063  tr_variantDictAddBool (d, TR_KEY_script_torrent_done_enabled, tr_sessionIsTorrentDoneScriptEnabled (s));
2064  tr_variantDictAddBool (d, TR_KEY_queue_stalled_enabled, tr_sessionGetQueueStalledEnabled (s));
2065  tr_variantDictAddInt  (d, TR_KEY_queue_stalled_minutes, tr_sessionGetQueueStalledMinutes (s));
2066  tr_formatter_get_units (tr_variantDictAddDict (d, TR_KEY_units, 0));
2067  tr_variantDictAddStr  (d, TR_KEY_version, LONG_VERSION_STRING);
2068  switch (tr_sessionGetEncryption (s))
2069    {
2070      case TR_CLEAR_PREFERRED: str = "tolerated"; break;
2071      case TR_ENCRYPTION_REQUIRED: str = "required"; break;
2072      default: str = "preferred"; break;
2073    }
2074  tr_variantDictAddStr (d, TR_KEY_encryption, str);
2075
2076  return NULL;
2077}
2078
2079static const char*
2080freeSpace (tr_session               * session,
2081           tr_variant               * args_in,
2082           tr_variant               * args_out,
2083           struct tr_rpc_idle_data  * idle_data UNUSED)
2084{
2085  int tmperr;
2086  const char * path = NULL;
2087  const char * err = NULL;
2088  int64_t free_space = -1;
2089
2090  /* get the free space */
2091  tr_variantDictFindStr (args_in, TR_KEY_path, &path, NULL);
2092  tmperr = errno;
2093  errno = 0;
2094  free_space = tr_sessionGetDirFreeSpace (session, path);
2095  if (free_space < 0)
2096    err = tr_strerror (errno);
2097  errno = tmperr;
2098
2099  /* response */
2100  if (path != NULL)
2101    tr_variantDictAddStr (args_out, TR_KEY_path, path);
2102  tr_variantDictAddInt (args_out, TR_KEY_size_bytes, free_space);
2103  return err;
2104}
2105
2106/***
2107****
2108***/
2109
2110static const char*
2111sessionClose (tr_session               * session,
2112              tr_variant               * args_in UNUSED,
2113              tr_variant               * args_out UNUSED,
2114              struct tr_rpc_idle_data  * idle_data UNUSED)
2115{
2116  notify (session, TR_RPC_SESSION_CLOSE, NULL);
2117  return NULL;
2118}
2119
2120/***
2121****
2122***/
2123
2124typedef const char* (*handler)(tr_session*, tr_variant*, tr_variant*, struct tr_rpc_idle_data *);
2125
2126static struct method
2127{
2128  const char *  name;
2129  bool          immediate;
2130  handler       func;
2131}
2132methods[] =
2133{
2134  { "port-test",             false, portTest            },
2135  { "blocklist-update",      false, blocklistUpdate     },
2136  { "free-space",            true,  freeSpace           },
2137  { "session-close",         true,  sessionClose        },
2138  { "session-get",           true,  sessionGet          },
2139  { "session-set",           true,  sessionSet          },
2140  { "session-stats",         true,  sessionStats        },
2141  { "torrent-add",           false, torrentAdd          },
2142  { "torrent-get",           true,  torrentGet          },
2143  { "torrent-remove",        true,  torrentRemove       },
2144  { "torrent-rename-path",   false, torrentRenamePath   },
2145  { "torrent-set",           true,  torrentSet          },
2146  { "torrent-set-location",  true,  torrentSetLocation  },
2147  { "torrent-start",         true,  torrentStart        },
2148  { "torrent-start-now",     true,  torrentStartNow     },
2149  { "torrent-stop",          true,  torrentStop         },
2150  { "torrent-verify",        true,  torrentVerify       },
2151  { "torrent-reannounce",    true,  torrentReannounce   },
2152  { "queue-move-top",        true,  queueMoveTop        },
2153  { "queue-move-up",         true,  queueMoveUp         },
2154  { "queue-move-down",       true,  queueMoveDown       },
2155  { "queue-move-bottom",     true,  queueMoveBottom     }
2156};
2157
2158static void
2159noop_response_callback (tr_session       * session UNUSED,
2160                        struct evbuffer  * response UNUSED,
2161                        void             * user_data UNUSED)
2162{
2163}
2164
2165static void
2166request_exec (tr_session             * session,
2167              tr_variant             * request,
2168              tr_rpc_response_func     callback,
2169              void                   * callback_user_data)
2170{
2171  int i;
2172  const char * str;
2173  tr_variant * args_in = tr_variantDictFind (request, TR_KEY_arguments);
2174  const char * result = NULL;
2175
2176  if (callback == NULL)
2177    callback = noop_response_callback;
2178
2179  /* parse the request */
2180  if (!tr_variantDictFindStr (request, TR_KEY_method, &str, NULL))
2181    {
2182      result = "no method name";
2183    }
2184  else
2185    {
2186      const int n = TR_N_ELEMENTS (methods);
2187
2188      for (i=0; i<n; ++i)
2189        if (!strcmp (str, methods[i].name))
2190          break;
2191
2192      if (i ==n)
2193        result = "method name not recognized";
2194    }
2195
2196  /* if we couldn't figure out which method to use, return an error */
2197  if (result != NULL)
2198    {
2199      int64_t tag;
2200      tr_variant response;
2201      struct evbuffer * buf;
2202
2203      tr_variantInitDict (&response, 3);
2204      tr_variantDictAddDict (&response, TR_KEY_arguments, 0);
2205      tr_variantDictAddStr (&response, TR_KEY_result, result);
2206      if (tr_variantDictFindInt (request, TR_KEY_tag, &tag))
2207        tr_variantDictAddInt (&response, TR_KEY_tag, tag);
2208
2209      buf = tr_variantToBuf (&response, TR_VARIANT_FMT_JSON_LEAN);
2210      (*callback)(session, buf, callback_user_data);
2211      evbuffer_free (buf);
2212
2213      tr_variantFree (&response);
2214    }
2215  else if (methods[i].immediate)
2216    {
2217      int64_t tag;
2218      tr_variant response;
2219      tr_variant * args_out;
2220      struct evbuffer * buf;
2221
2222      tr_variantInitDict (&response, 3);
2223      args_out = tr_variantDictAddDict (&response, TR_KEY_arguments, 0);
2224      result = (*methods[i].func)(session, args_in, args_out, NULL);
2225      if (result == NULL)
2226        result = "success";
2227      tr_variantDictAddStr (&response, TR_KEY_result, result);
2228      if (tr_variantDictFindInt (request, TR_KEY_tag, &tag))
2229        tr_variantDictAddInt (&response, TR_KEY_tag, tag);
2230
2231      buf = tr_variantToBuf (&response, TR_VARIANT_FMT_JSON_LEAN);
2232      (*callback)(session, buf, callback_user_data);
2233      evbuffer_free (buf);
2234
2235      tr_variantFree (&response);
2236    }
2237  else
2238    {
2239      int64_t tag;
2240      struct tr_rpc_idle_data * data = tr_new0 (struct tr_rpc_idle_data, 1);
2241      data->session = session;
2242      data->response = tr_new0 (tr_variant, 1);
2243      tr_variantInitDict (data->response, 3);
2244      if (tr_variantDictFindInt (request, TR_KEY_tag, &tag))
2245        tr_variantDictAddInt (data->response, TR_KEY_tag, tag);
2246      data->args_out = tr_variantDictAddDict (data->response, TR_KEY_arguments, 0);
2247      data->callback = callback;
2248      data->callback_user_data = callback_user_data;
2249      (*methods[i].func)(session, args_in, data->args_out, data);
2250    }
2251}
2252
2253void
2254tr_rpc_request_exec_json (tr_session            * session,
2255                          const void            * request_json,
2256                          int                     request_len,
2257                          tr_rpc_response_func    callback,
2258                          void                  * callback_user_data)
2259{
2260  tr_variant top;
2261  int have_content;
2262
2263  if (request_len < 0)
2264    request_len = strlen (request_json);
2265
2266  have_content = !tr_variantFromJson (&top, request_json, request_len);
2267  request_exec (session, have_content ? &top : NULL, callback, callback_user_data);
2268
2269  if (have_content)
2270    tr_variantFree (&top);
2271}
2272
2273/**
2274 * Munge the URI into a usable form.
2275 *
2276 * We have very loose typing on this to make the URIs as simple as possible:
2277 * - anything not a 'tag' or 'method' is automatically in 'arguments'
2278 * - values that are all-digits are numbers
2279 * - values that are all-digits or commas are number lists
2280 * - all other values are strings
2281 */
2282void
2283tr_rpc_parse_list_str (tr_variant  * setme,
2284                       const char  * str,
2285                       int           len)
2286
2287{
2288  int valueCount;
2289  int * values = tr_parseNumberRange (str, len, &valueCount);
2290
2291  if (valueCount == 0)
2292    {
2293      tr_variantInitStr (setme, str, len);
2294    }
2295  else if (valueCount == 1)
2296    {
2297      tr_variantInitInt (setme, values[0]);
2298    }
2299  else
2300    {
2301      int i;
2302
2303      tr_variantInitList (setme, valueCount);
2304
2305      for (i=0; i<valueCount; ++i)
2306        tr_variantListAddInt (setme, values[i]);
2307    }
2308
2309  tr_free (values);
2310}
2311
2312void
2313tr_rpc_request_exec_uri (tr_session           * session,
2314                         const void           * request_uri,
2315                         int                    request_len,
2316                         tr_rpc_response_func   callback,
2317                         void                 * callback_user_data)
2318{
2319  const char * pch;
2320  tr_variant top;
2321  tr_variant * args;
2322  char * request = tr_strndup (request_uri, request_len);
2323
2324  tr_variantInitDict (&top, 3);
2325  args = tr_variantDictAddDict (&top, TR_KEY_arguments, 0);
2326
2327  pch = strchr (request, '?');
2328  if (!pch) pch = request;
2329  while (pch)
2330    {
2331      const char * delim = strchr (pch, '=');
2332      const char * next = strchr (pch, '&');
2333      if (delim)
2334        {
2335          char * key = tr_strndup (pch, delim - pch);
2336          int isArg = strcmp (key, "method") && strcmp (key, "tag");
2337          tr_variant * parent = isArg ? args : &top;
2338
2339          tr_rpc_parse_list_str (tr_variantDictAdd (parent, tr_quark_new (key, delim-pch)),
2340                                 delim + 1,
2341                                 next ? (size_t)(next - (delim + 1)) : strlen (delim + 1));
2342          tr_free (key);
2343        }
2344
2345      pch = next ? next + 1 : NULL;
2346    }
2347
2348  request_exec (session, &top, callback, callback_user_data);
2349
2350  /* cleanup */
2351  tr_variantFree (&top);
2352  tr_free (request);
2353}
Note: See TracBrowser for help on using the repository browser.