source: trunk/libtransmission/rpcimpl.c @ 14209

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

(trunk, libT) #5450: when starting torrents via RPC with torrent-start or torrent-start-now requests, work through the rpc-supplied torrent list in queue order

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