source: trunk/libtransmission/rpcimpl.c

Last change on this file was 14718, checked in by mikedld, 5 years ago

Explicitly compare result of str(n)cmp/memcmp to signify that it's not boolean

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