source: trunk/libtransmission/rpcimpl.c @ 13815

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

(libT) #5234 'typo causing RPC session-get error in nightlies': add a fix, which causes r13814's test to pass

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