source: trunk/libtransmission/rpcimpl.c @ 14322

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

since libz's been required in rpcimpl.c for ages and nobody's complained, remove the HAVE_ZLIB cruft for conditional-compiling with and without zlib.

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