source: trunk/libtransmission/rpcimpl.c @ 13868

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

make all the log functions/structs/enums use a single 'tr_log' namespace, such as tr_logGetQueue, tr_logAddInfo, tr_logIsLevelActive

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