source: trunk/libtransmission/rpcimpl.c @ 14428

Last change on this file since 14428 was 14428, checked in by mikedld, 6 years ago

Replace tabs with spaces; remove trailing spaces

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