Transmission: update to 2.82
[tomato.git] / release / src / router / transmission / libtransmission / rpcimpl.c
blob120fb68950df2b87486177e58863b09079eeafb6
1 /*
2 * This file Copyright (C) Mnemosyne LLC
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.
10 * $Id: rpcimpl.c 14130 2013-07-20 15:37:13Z jordan $
13 #include <assert.h>
14 #include <ctype.h> /* isdigit */
15 #include <errno.h>
16 #include <stdlib.h> /* strtol */
17 #include <string.h> /* strcmp */
19 #ifdef HAVE_ZLIB
20 #include <zlib.h>
21 #endif
23 #include <event2/buffer.h>
25 #include "transmission.h"
26 #include "completion.h"
27 #include "fdlimit.h"
28 #include "log.h"
29 #include "platform-quota.h" /* tr_device_info_get_free_space() */
30 #include "rpcimpl.h"
31 #include "session.h"
32 #include "torrent.h"
33 #include "utils.h"
34 #include "variant.h"
35 #include "version.h"
36 #include "web.h"
38 #define RPC_VERSION 15
39 #define RPC_VERSION_MIN 1
41 #define RECENTLY_ACTIVE_SECONDS 60
43 #define TR_N_ELEMENTS(ary)(sizeof (ary) / sizeof (*ary))
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
62 /***
63 ****
64 ***/
66 static tr_rpc_callback_status
67 notify (tr_session * session,
68 int type,
69 tr_torrent * tor)
71 tr_rpc_callback_status status = 0;
73 if (session->rpc_func)
74 status = session->rpc_func (session, type, tor,
75 session->rpc_func_user_data);
77 return status;
80 /***
81 ****
82 ***/
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 */
87 struct tr_rpc_idle_data
89 tr_session * session;
90 tr_variant * response;
91 tr_variant * args_out;
92 tr_rpc_response_func callback;
93 void * callback_user_data;
96 static void
97 tr_idle_function_done (struct tr_rpc_idle_data * data, const char * result)
99 struct evbuffer * buf;
101 if (result == NULL)
102 result = "success";
103 tr_variantDictAddStr (data->response, TR_KEY_result, result);
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);
109 tr_variantFree (data->response);
110 tr_free (data->response);
111 tr_free (data);
114 /***
115 ****
116 ***/
118 static tr_torrent **
119 getTorrents (tr_session * session,
120 tr_variant * args,
121 int * setmeCount)
123 int torrentCount = 0;
124 int64_t id;
125 tr_torrent ** torrents = NULL;
126 tr_variant * ids;
127 const char * str;
129 if (tr_variantDictFindList (args, TR_KEY_ids, &ids))
131 int i;
132 const int n = tr_variantListSize (ids);
134 torrents = tr_new0 (tr_torrent *, n);
136 for (i=0; i<n; ++i)
138 const char * str;
139 tr_torrent * tor;
140 tr_variant * node = tr_variantListChild (ids, i);
142 if (tr_variantGetInt (node, &id))
143 tor = tr_torrentFindFromId (session, id);
144 else if (tr_variantGetStr (node, &str, NULL))
145 tor = tr_torrentFindFromHashString (session, str);
146 else
147 tor = NULL;
149 if (tor != NULL)
150 torrents[torrentCount++] = tor;
153 else if (tr_variantDictFindInt (args, TR_KEY_ids, &id)
154 || tr_variantDictFindInt (args, TR_KEY_id, &id))
156 tr_torrent * tor;
157 torrents = tr_new0 (tr_torrent *, 1);
158 if ((tor = tr_torrentFindFromId (session, id)))
159 torrents[torrentCount++] = tor;
161 else if (tr_variantDictFindStr (args, TR_KEY_ids, &str, NULL))
163 if (!strcmp (str, "recently-active"))
165 tr_torrent * tor = NULL;
166 const time_t now = tr_time ();
167 const time_t window = RECENTLY_ACTIVE_SECONDS;
168 const int n = tr_sessionCountTorrents (session);
169 torrents = tr_new0 (tr_torrent *, n);
170 while ((tor = tr_torrentNext (session, tor)))
171 if (tor->anyDate >= now - window)
172 torrents[torrentCount++] = tor;
174 else
176 tr_torrent * tor;
177 torrents = tr_new0 (tr_torrent *, 1);
178 if ((tor = tr_torrentFindFromHashString (session, str)))
179 torrents[torrentCount++] = tor;
182 else /* all of them */
184 torrents = tr_sessionGetTorrents (session, &torrentCount);
187 *setmeCount = torrentCount;
188 return torrents;
191 static void
192 notifyBatchQueueChange (tr_session * session, tr_torrent ** torrents, int n)
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);
200 static const char*
201 queueMoveTop (tr_session * session,
202 tr_variant * args_in,
203 tr_variant * args_out UNUSED,
204 struct tr_rpc_idle_data * idle_data UNUSED)
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;
214 static const char*
215 queueMoveUp (tr_session * session,
216 tr_variant * args_in,
217 tr_variant * args_out UNUSED,
218 struct tr_rpc_idle_data * idle_data UNUSED)
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;
228 static const char*
229 queueMoveDown (tr_session * session,
230 tr_variant * args_in,
231 tr_variant * args_out UNUSED,
232 struct tr_rpc_idle_data * idle_data UNUSED)
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;
242 static const char*
243 queueMoveBottom (tr_session * session,
244 tr_variant * args_in,
245 tr_variant * args_out UNUSED,
246 struct tr_rpc_idle_data * idle_data UNUSED)
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;
256 static const char*
257 torrentStart (tr_session * session,
258 tr_variant * args_in,
259 tr_variant * args_out UNUSED,
260 struct tr_rpc_idle_data * idle_data UNUSED)
262 int i;
263 int torrentCount;
264 tr_torrent ** torrents;
266 assert (idle_data == NULL);
268 torrents = getTorrents (session, args_in, &torrentCount);
269 for (i=0; i<torrentCount; ++i)
271 tr_torrent * tor = torrents[i];
272 if (!tor->isRunning)
274 tr_torrentStart (tor);
275 notify (session, TR_RPC_TORRENT_STARTED, tor);
279 tr_free (torrents);
280 return NULL;
283 static const char*
284 torrentStartNow (tr_session * session,
285 tr_variant * args_in,
286 tr_variant * args_out UNUSED,
287 struct tr_rpc_idle_data * idle_data UNUSED)
289 int i;
290 int torrentCount;
291 tr_torrent ** torrents;
293 assert (idle_data == NULL);
295 torrents = getTorrents (session, args_in, &torrentCount);
296 for (i=0; i<torrentCount; ++i)
298 tr_torrent * tor = torrents[i];
300 if (!tor->isRunning)
302 tr_torrentStartNow (tor);
303 notify (session, TR_RPC_TORRENT_STARTED, tor);
307 tr_free (torrents);
308 return NULL;
311 static const char*
312 torrentStop (tr_session * session,
313 tr_variant * args_in,
314 tr_variant * args_out UNUSED,
315 struct tr_rpc_idle_data * idle_data UNUSED)
317 int i;
318 int torrentCount;
319 tr_torrent ** torrents;
321 assert (idle_data == NULL);
323 torrents = getTorrents (session, args_in, &torrentCount);
324 for (i=0; i<torrentCount; ++i)
326 tr_torrent * tor = torrents[i];
328 if (tor->isRunning || tr_torrentIsQueued (tor))
330 tor->isStopping = true;
331 notify (session, TR_RPC_TORRENT_STOPPED, tor);
335 tr_free (torrents);
336 return NULL;
339 static const char*
340 torrentRemove (tr_session * session,
341 tr_variant * args_in,
342 tr_variant * args_out UNUSED,
343 struct tr_rpc_idle_data * idle_data UNUSED)
345 int i;
346 int torrentCount;
347 tr_rpc_callback_type type;
348 bool deleteFlag = false;
349 tr_torrent ** torrents;
351 assert (idle_data == NULL);
353 tr_variantDictFindBool (args_in, TR_KEY_delete_local_data, &deleteFlag);
354 type = deleteFlag ? TR_RPC_TORRENT_TRASHING
355 : TR_RPC_TORRENT_REMOVING;
357 torrents = getTorrents (session, args_in, &torrentCount);
358 for (i=0; i<torrentCount; ++i)
360 tr_torrent * tor = torrents[i];
361 const tr_rpc_callback_status status = notify (session, type, tor);
363 if (!(status & TR_RPC_NOREMOVE))
364 tr_torrentRemove (tor, deleteFlag, NULL);
367 tr_free (torrents);
368 return NULL;
371 static const char*
372 torrentReannounce (tr_session * session,
373 tr_variant * args_in,
374 tr_variant * args_out UNUSED,
375 struct tr_rpc_idle_data * idle_data UNUSED)
377 int i;
378 int torrentCount;
379 tr_torrent ** torrents;
381 assert (idle_data == NULL);
383 torrents = getTorrents (session, args_in, &torrentCount);
384 for (i=0; i<torrentCount; ++i)
386 tr_torrent * tor = torrents[i];
388 if (tr_torrentCanManualUpdate (tor))
390 tr_torrentManualUpdate (tor);
391 notify (session, TR_RPC_TORRENT_CHANGED, tor);
395 tr_free (torrents);
396 return NULL;
399 static const char*
400 torrentVerify (tr_session * session,
401 tr_variant * args_in,
402 tr_variant * args_out UNUSED,
403 struct tr_rpc_idle_data * idle_data UNUSED)
405 int i;
406 int torrentCount;
407 tr_torrent ** torrents;
409 assert (idle_data == NULL);
411 torrents = getTorrents (session, args_in, &torrentCount);
412 for (i=0; i<torrentCount; ++i)
414 tr_torrent * tor = torrents[i];
415 tr_torrentVerify (tor, NULL, NULL);
416 notify (session, TR_RPC_TORRENT_CHANGED, tor);
419 tr_free (torrents);
420 return NULL;
423 /***
424 ****
425 ***/
427 static void
428 addFileStats (const tr_torrent * tor, tr_variant * list)
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);
435 for (i=0; i<info->fileCount; ++i)
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_priority, file->priority);
441 tr_variantDictAddBool (d, TR_KEY_wanted, !file->dnd);
444 tr_torrentFilesFree (files, n);
447 static void
448 addFiles (const tr_torrent * tor, tr_variant * list)
450 tr_file_index_t i;
451 tr_file_index_t n;
452 const tr_info * info = tr_torrentInfo (tor);
453 tr_file_stat * files = tr_torrentFiles (tor, &n);
455 for (i=0; i<info->fileCount; ++i)
457 const tr_file * file = &info->files[i];
458 tr_variant * d = tr_variantListAddDict (list, 3);
459 tr_variantDictAddInt (d, TR_KEY_bytesCompleted, files[i].bytesCompleted);
460 tr_variantDictAddInt (d, TR_KEY_length, file->length);
461 tr_variantDictAddStr (d, TR_KEY_name, file->name);
464 tr_torrentFilesFree (files, n);
467 static void
468 addWebseeds (const tr_info * info,
469 tr_variant * webseeds)
471 unsigned int i;
473 for (i=0; i< info->webseedCount; ++i)
474 tr_variantListAddStr (webseeds, info->webseeds[i]);
477 static void
478 addTrackers (const tr_info * info,
479 tr_variant * trackers)
481 unsigned int i;
483 for (i=0; i<info->trackerCount; ++i)
485 const tr_tracker_info * t = &info->trackers[i];
486 tr_variant * d = tr_variantListAddDict (trackers, 4);
487 tr_variantDictAddStr (d, TR_KEY_announce, t->announce);
488 tr_variantDictAddInt (d, TR_KEY_id, t->id);
489 tr_variantDictAddStr (d, TR_KEY_scrape, t->scrape);
490 tr_variantDictAddInt (d, TR_KEY_tier, t->tier);
494 static void
495 addTrackerStats (const tr_tracker_stat * st, int n, tr_variant * list)
497 int i;
499 for (i=0; i<n; ++i)
501 const tr_tracker_stat * s = &st[i];
502 tr_variant * d = tr_variantListAddDict (list, 26);
503 tr_variantDictAddStr (d, TR_KEY_announce, s->announce);
504 tr_variantDictAddInt (d, TR_KEY_announceState, s->announceState);
505 tr_variantDictAddInt (d, TR_KEY_downloadCount, s->downloadCount);
506 tr_variantDictAddBool (d, TR_KEY_hasAnnounced, s->hasAnnounced);
507 tr_variantDictAddBool (d, TR_KEY_hasScraped, s->hasScraped);
508 tr_variantDictAddStr (d, TR_KEY_host, s->host);
509 tr_variantDictAddInt (d, TR_KEY_id, s->id);
510 tr_variantDictAddBool (d, TR_KEY_isBackup, s->isBackup);
511 tr_variantDictAddInt (d, TR_KEY_lastAnnouncePeerCount, s->lastAnnouncePeerCount);
512 tr_variantDictAddStr (d, TR_KEY_lastAnnounceResult, s->lastAnnounceResult);
513 tr_variantDictAddInt (d, TR_KEY_lastAnnounceStartTime, s->lastAnnounceStartTime);
514 tr_variantDictAddBool (d, TR_KEY_lastAnnounceSucceeded, s->lastAnnounceSucceeded);
515 tr_variantDictAddInt (d, TR_KEY_lastAnnounceTime, s->lastAnnounceTime);
516 tr_variantDictAddBool (d, TR_KEY_lastAnnounceTimedOut, s->lastAnnounceTimedOut);
517 tr_variantDictAddStr (d, TR_KEY_lastScrapeResult, s->lastScrapeResult);
518 tr_variantDictAddInt (d, TR_KEY_lastScrapeStartTime, s->lastScrapeStartTime);
519 tr_variantDictAddBool (d, TR_KEY_lastScrapeSucceeded, s->lastScrapeSucceeded);
520 tr_variantDictAddInt (d, TR_KEY_lastScrapeTime, s->lastScrapeTime);
521 tr_variantDictAddInt (d, TR_KEY_lastScrapeTimedOut, s->lastScrapeTimedOut);
522 tr_variantDictAddInt (d, TR_KEY_leecherCount, s->leecherCount);
523 tr_variantDictAddInt (d, TR_KEY_nextAnnounceTime, s->nextAnnounceTime);
524 tr_variantDictAddInt (d, TR_KEY_nextScrapeTime, s->nextScrapeTime);
525 tr_variantDictAddStr (d, TR_KEY_scrape, s->scrape);
526 tr_variantDictAddInt (d, TR_KEY_scrapeState, s->scrapeState);
527 tr_variantDictAddInt (d, TR_KEY_seederCount, s->seederCount);
528 tr_variantDictAddInt (d, TR_KEY_tier, s->tier);
532 static void
533 addPeers (tr_torrent * tor, tr_variant * list)
535 int i;
536 int peerCount;
537 tr_peer_stat * peers = tr_torrentPeers (tor, &peerCount);
539 tr_variantInitList (list, peerCount);
541 for (i=0; i<peerCount; ++i)
543 tr_variant * d = tr_variantListAddDict (list, 16);
544 const tr_peer_stat * peer = peers + i;
545 tr_variantDictAddStr (d, TR_KEY_address, peer->addr);
546 tr_variantDictAddStr (d, TR_KEY_clientName, peer->client);
547 tr_variantDictAddBool (d, TR_KEY_clientIsChoked, peer->clientIsChoked);
548 tr_variantDictAddBool (d, TR_KEY_clientIsInterested, peer->clientIsInterested);
549 tr_variantDictAddStr (d, TR_KEY_flagStr, peer->flagStr);
550 tr_variantDictAddBool (d, TR_KEY_isDownloadingFrom, peer->isDownloadingFrom);
551 tr_variantDictAddBool (d, TR_KEY_isEncrypted, peer->isEncrypted);
552 tr_variantDictAddBool (d, TR_KEY_isIncoming, peer->isIncoming);
553 tr_variantDictAddBool (d, TR_KEY_isUploadingTo, peer->isUploadingTo);
554 tr_variantDictAddBool (d, TR_KEY_isUTP, peer->isUTP);
555 tr_variantDictAddBool (d, TR_KEY_peerIsChoked, peer->peerIsChoked);
556 tr_variantDictAddBool (d, TR_KEY_peerIsInterested, peer->peerIsInterested);
557 tr_variantDictAddInt (d, TR_KEY_port, peer->port);
558 tr_variantDictAddReal (d, TR_KEY_progress, peer->progress);
559 tr_variantDictAddInt (d, TR_KEY_rateToClient, toSpeedBytes (peer->rateToClient_KBps));
560 tr_variantDictAddInt (d, TR_KEY_rateToPeer, toSpeedBytes (peer->rateToPeer_KBps));
563 tr_torrentPeersFree (peers, peerCount);
566 static void
567 addField (tr_torrent * const tor,
568 const tr_info * const inf,
569 const tr_stat * const st,
570 tr_variant * const d,
571 const tr_quark key)
573 char * str;
575 switch (key)
577 case TR_KEY_activityDate:
578 tr_variantDictAddInt (d, key, st->activityDate);
579 break;
581 case TR_KEY_addedDate:
582 tr_variantDictAddInt (d, key, st->addedDate);
583 break;
585 case TR_KEY_bandwidthPriority:
586 tr_variantDictAddInt (d, key, tr_torrentGetPriority (tor));
587 break;
589 case TR_KEY_comment:
590 tr_variantDictAddStr (d, key, inf->comment ? inf->comment : "");
591 break;
593 case TR_KEY_corruptEver:
594 tr_variantDictAddInt (d, key, st->corruptEver);
595 break;
597 case TR_KEY_creator:
598 tr_variantDictAddStr (d, key, inf->creator ? inf->creator : "");
599 break;
601 case TR_KEY_dateCreated:
602 tr_variantDictAddInt (d, key, inf->dateCreated);
603 break;
605 case TR_KEY_desiredAvailable:
606 tr_variantDictAddInt (d, key, st->desiredAvailable);
607 break;
609 case TR_KEY_doneDate:
610 tr_variantDictAddInt (d, key, st->doneDate);
611 break;
613 case TR_KEY_downloadDir:
614 tr_variantDictAddStr (d, key, tr_torrentGetDownloadDir (tor));
615 break;
617 case TR_KEY_downloadedEver:
618 tr_variantDictAddInt (d, key, st->downloadedEver);
619 break;
621 case TR_KEY_downloadLimit:
622 tr_variantDictAddInt (d, key, tr_torrentGetSpeedLimit_KBps (tor, TR_DOWN));
623 break;
625 case TR_KEY_downloadLimited:
626 tr_variantDictAddBool (d, key, tr_torrentUsesSpeedLimit (tor, TR_DOWN));
627 break;
629 case TR_KEY_error:
630 tr_variantDictAddInt (d, key, st->error);
631 break;
633 case TR_KEY_errorString:
634 tr_variantDictAddStr (d, key, st->errorString);
635 break;
637 case TR_KEY_eta:
638 tr_variantDictAddInt (d, key, st->eta);
639 break;
641 case TR_KEY_files:
642 addFiles (tor, tr_variantDictAddList (d, key, inf->fileCount));
643 break;
645 case TR_KEY_fileStats:
646 addFileStats (tor, tr_variantDictAddList (d, key, inf->fileCount));
647 break;
649 case TR_KEY_hashString:
650 tr_variantDictAddStr (d, key, tor->info.hashString);
651 break;
653 case TR_KEY_haveUnchecked:
654 tr_variantDictAddInt (d, key, st->haveUnchecked);
655 break;
657 case TR_KEY_haveValid:
658 tr_variantDictAddInt (d, key, st->haveValid);
659 break;
661 case TR_KEY_honorsSessionLimits:
662 tr_variantDictAddBool (d, key, tr_torrentUsesSessionLimits (tor));
663 break;
665 case TR_KEY_id:
666 tr_variantDictAddInt (d, key, st->id);
667 break;
669 case TR_KEY_isFinished:
670 tr_variantDictAddBool (d, key, st->finished);
671 break;
673 case TR_KEY_isPrivate:
674 tr_variantDictAddBool (d, key, tr_torrentIsPrivate (tor));
675 break;
677 case TR_KEY_isStalled:
678 tr_variantDictAddBool (d, key, st->isStalled);
679 break;
681 case TR_KEY_leftUntilDone:
682 tr_variantDictAddInt (d, key, st->leftUntilDone);
683 break;
685 case TR_KEY_manualAnnounceTime:
686 tr_variantDictAddInt (d, key, st->manualAnnounceTime);
687 break;
689 case TR_KEY_maxConnectedPeers:
690 tr_variantDictAddInt (d, key, tr_torrentGetPeerLimit (tor));
691 break;
693 case TR_KEY_magnetLink:
694 str = tr_torrentGetMagnetLink (tor);
695 tr_variantDictAddStr (d, key, str);
696 tr_free (str);
697 break;
699 case TR_KEY_metadataPercentComplete:
700 tr_variantDictAddReal (d, key, st->metadataPercentComplete);
701 break;
703 case TR_KEY_name:
704 tr_variantDictAddStr (d, key, tr_torrentName (tor));
705 break;
707 case TR_KEY_percentDone:
708 tr_variantDictAddReal (d, key, st->percentDone);
709 break;
711 case TR_KEY_peer_limit:
712 tr_variantDictAddInt (d, key, tr_torrentGetPeerLimit (tor));
713 break;
715 case TR_KEY_peers:
716 addPeers (tor, tr_variantDictAdd (d, key));
717 break;
719 case TR_KEY_peersConnected:
720 tr_variantDictAddInt (d, key, st->peersConnected);
721 break;
723 case TR_KEY_peersFrom:
725 tr_variant * tmp = tr_variantDictAddDict (d, key, 7);
726 const int * f = st->peersFrom;
727 tr_variantDictAddInt (tmp, TR_KEY_fromCache, f[TR_PEER_FROM_RESUME]);
728 tr_variantDictAddInt (tmp, TR_KEY_fromDht, f[TR_PEER_FROM_DHT]);
729 tr_variantDictAddInt (tmp, TR_KEY_fromIncoming, f[TR_PEER_FROM_INCOMING]);
730 tr_variantDictAddInt (tmp, TR_KEY_fromLpd, f[TR_PEER_FROM_LPD]);
731 tr_variantDictAddInt (tmp, TR_KEY_fromLtep, f[TR_PEER_FROM_LTEP]);
732 tr_variantDictAddInt (tmp, TR_KEY_fromPex, f[TR_PEER_FROM_PEX]);
733 tr_variantDictAddInt (tmp, TR_KEY_fromTracker, f[TR_PEER_FROM_TRACKER]);
734 break;
737 case TR_KEY_peersGettingFromUs:
738 tr_variantDictAddInt (d, key, st->peersGettingFromUs);
739 break;
741 case TR_KEY_peersSendingToUs:
742 tr_variantDictAddInt (d, key, st->peersSendingToUs);
743 break;
745 case TR_KEY_pieces:
746 if (tr_torrentHasMetadata (tor))
748 size_t byte_count = 0;
749 void * bytes = tr_cpCreatePieceBitfield (&tor->completion, &byte_count);
750 char * str = tr_base64_encode (bytes, byte_count, NULL);
751 tr_variantDictAddStr (d, key, str!=NULL ? str : "");
752 tr_free (str);
753 tr_free (bytes);
755 else
757 tr_variantDictAddStr (d, key, "");
759 break;
761 case TR_KEY_pieceCount:
762 tr_variantDictAddInt (d, key, inf->pieceCount);
763 break;
765 case TR_KEY_pieceSize:
766 tr_variantDictAddInt (d, key, inf->pieceSize);
767 break;
769 case TR_KEY_priorities:
771 tr_file_index_t i;
772 tr_variant * p = tr_variantDictAddList (d, key, inf->fileCount);
773 for (i=0; i<inf->fileCount; ++i)
774 tr_variantListAddInt (p, inf->files[i].priority);
775 break;
778 case TR_KEY_queuePosition:
779 tr_variantDictAddInt (d, key, st->queuePosition);
780 break;
782 case TR_KEY_etaIdle:
783 tr_variantDictAddInt (d, key, st->etaIdle);
784 break;
786 case TR_KEY_rateDownload:
787 tr_variantDictAddInt (d, key, toSpeedBytes (st->pieceDownloadSpeed_KBps));
788 break;
790 case TR_KEY_rateUpload:
791 tr_variantDictAddInt (d, key, toSpeedBytes (st->pieceUploadSpeed_KBps));
792 break;
794 case TR_KEY_recheckProgress:
795 tr_variantDictAddReal (d, key, st->recheckProgress);
796 break;
798 case TR_KEY_seedIdleLimit:
799 tr_variantDictAddInt (d, key, tr_torrentGetIdleLimit (tor));
800 break;
802 case TR_KEY_seedIdleMode:
803 tr_variantDictAddInt (d, key, tr_torrentGetIdleMode (tor));
804 break;
806 case TR_KEY_seedRatioLimit:
807 tr_variantDictAddReal (d, key, tr_torrentGetRatioLimit (tor));
808 break;
810 case TR_KEY_seedRatioMode:
811 tr_variantDictAddInt (d, key, tr_torrentGetRatioMode (tor));
812 break;
814 case TR_KEY_sizeWhenDone:
815 tr_variantDictAddInt (d, key, st->sizeWhenDone);
816 break;
818 case TR_KEY_startDate:
819 tr_variantDictAddInt (d, key, st->startDate);
820 break;
822 case TR_KEY_status:
823 tr_variantDictAddInt (d, key, st->activity);
824 break;
826 case TR_KEY_secondsDownloading:
827 tr_variantDictAddInt (d, key, st->secondsDownloading);
828 break;
830 case TR_KEY_secondsSeeding:
831 tr_variantDictAddInt (d, key, st->secondsSeeding);
832 break;
834 case TR_KEY_trackers:
835 addTrackers (inf, tr_variantDictAddList (d, key, inf->trackerCount));
836 break;
838 case TR_KEY_trackerStats:
840 int n;
841 tr_tracker_stat * s = tr_torrentTrackers (tor, &n);
842 addTrackerStats (s, n, tr_variantDictAddList (d, key, n));
843 tr_torrentTrackersFree (s, n);
844 break;
847 case TR_KEY_torrentFile:
848 tr_variantDictAddStr (d, key, inf->torrent);
849 break;
851 case TR_KEY_totalSize:
852 tr_variantDictAddInt (d, key, inf->totalSize);
853 break;
855 case TR_KEY_uploadedEver:
856 tr_variantDictAddInt (d, key, st->uploadedEver);
857 break;
859 case TR_KEY_uploadLimit:
860 tr_variantDictAddInt (d, key, tr_torrentGetSpeedLimit_KBps (tor, TR_UP));
861 break;
863 case TR_KEY_uploadLimited:
864 tr_variantDictAddBool (d, key, tr_torrentUsesSpeedLimit (tor, TR_UP));
865 break;
867 case TR_KEY_uploadRatio:
868 tr_variantDictAddReal (d, key, st->ratio);
869 break;
871 case TR_KEY_wanted:
873 tr_file_index_t i;
874 tr_variant * w = tr_variantDictAddList (d, key, inf->fileCount);
875 for (i=0; i<inf->fileCount; ++i)
876 tr_variantListAddInt (w, inf->files[i].dnd ? 0 : 1);
877 break;
880 case TR_KEY_webseeds:
881 addWebseeds (inf, tr_variantDictAddList (d, key, inf->webseedCount));
882 break;
884 case TR_KEY_webseedsSendingToUs:
885 tr_variantDictAddInt (d, key, st->webseedsSendingToUs);
886 break;
888 default:
889 break;
893 static void
894 addInfo (tr_torrent * tor, tr_variant * d, tr_variant * fields)
896 const int n = tr_variantListSize (fields);
898 tr_variantInitDict (d, n);
900 if (n > 0)
902 int i;
903 const tr_info const * inf = tr_torrentInfo (tor);
904 const tr_stat const * st = tr_torrentStat ((tr_torrent*)tor);
906 for (i=0; i<n; ++i)
908 size_t len;
909 const char * str;
910 if (tr_variantGetStr (tr_variantListChild (fields, i), &str, &len))
911 addField (tor, inf, st, d, tr_quark_new (str, len));
916 static const char*
917 torrentGet (tr_session * session,
918 tr_variant * args_in,
919 tr_variant * args_out,
920 struct tr_rpc_idle_data * idle_data UNUSED)
922 int i;
923 int torrentCount;
924 tr_torrent ** torrents = getTorrents (session, args_in, &torrentCount);
925 tr_variant * list = tr_variantDictAddList (args_out, TR_KEY_torrents, torrentCount);
926 tr_variant * fields;
927 const char * strVal;
928 const char * errmsg = NULL;
930 assert (idle_data == NULL);
932 if (tr_variantDictFindStr (args_in, TR_KEY_ids, &strVal, NULL) && !strcmp (strVal, "recently-active"))
934 int n = 0;
935 tr_variant * d;
936 const time_t now = tr_time ();
937 const int interval = RECENTLY_ACTIVE_SECONDS;
938 tr_variant * removed_out = tr_variantDictAddList (args_out, TR_KEY_removed, 0);
939 while ((d = tr_variantListChild (&session->removedTorrents, n++)))
941 int64_t intVal;
942 if (tr_variantDictFindInt (d, TR_KEY_date, &intVal) && (intVal >= now - interval))
944 tr_variantDictFindInt (d, TR_KEY_id, &intVal);
945 tr_variantListAddInt (removed_out, intVal);
950 if (!tr_variantDictFindList (args_in, TR_KEY_fields, &fields))
951 errmsg = "no fields specified";
952 else for (i=0; i<torrentCount; ++i)
953 addInfo (torrents[i], tr_variantListAdd (list), fields);
955 tr_free (torrents);
956 return errmsg;
959 /***
960 ****
961 ***/
963 static const char*
964 setFilePriorities (tr_torrent * tor,
965 int priority,
966 tr_variant * list)
968 int i;
969 int64_t tmp;
970 int fileCount = 0;
971 const int n = tr_variantListSize (list);
972 const char * errmsg = NULL;
973 tr_file_index_t * files = tr_new0 (tr_file_index_t, tor->info.fileCount);
975 if (n)
977 for (i=0; i<n; ++i)
979 if (tr_variantGetInt (tr_variantListChild (list, i), &tmp))
981 if (0 <= tmp && tmp < tor->info.fileCount)
982 files[fileCount++] = tmp;
983 else
984 errmsg = "file index out of range";
988 else /* if empty set, apply to all */
990 tr_file_index_t t;
991 for (t=0; t<tor->info.fileCount; ++t)
992 files[fileCount++] = t;
995 if (fileCount)
996 tr_torrentSetFilePriorities (tor, files, fileCount, priority);
998 tr_free (files);
999 return errmsg;
1002 static const char*
1003 setFileDLs (tr_torrent * tor,
1004 int do_download,
1005 tr_variant * list)
1007 int i;
1008 int64_t tmp;
1009 int fileCount = 0;
1010 const int n = tr_variantListSize (list);
1011 const char * errmsg = NULL;
1012 tr_file_index_t * files = tr_new0 (tr_file_index_t, tor->info.fileCount);
1014 if (n) /* if argument list, process them */
1016 for (i=0; i<n; ++i)
1018 if (tr_variantGetInt (tr_variantListChild (list, i), &tmp))
1020 if (0 <= tmp && tmp < tor->info.fileCount)
1021 files[fileCount++] = tmp;
1022 else
1023 errmsg = "file index out of range";
1027 else /* if empty set, apply to all */
1029 tr_file_index_t t;
1031 for (t=0; t<tor->info.fileCount; ++t)
1032 files[fileCount++] = t;
1035 if (fileCount)
1036 tr_torrentSetFileDLs (tor, files, fileCount, do_download);
1038 tr_free (files);
1039 return errmsg;
1042 static bool
1043 findAnnounceUrl (const tr_tracker_info * t, int n, const char * url, int * pos)
1045 int i;
1046 bool found = false;
1048 for (i=0; i<n; ++i)
1050 if (!strcmp (t[i].announce, url))
1052 found = true;
1054 if (pos != NULL)
1055 *pos = i;
1057 break;
1061 return found;
1064 static int
1065 copyTrackers (tr_tracker_info * tgt, const tr_tracker_info * src, int n)
1067 int i;
1068 int maxTier = -1;
1070 for (i=0; i<n; ++i)
1072 tgt[i].tier = src[i].tier;
1073 tgt[i].announce = tr_strdup (src[i].announce);
1074 maxTier = MAX (maxTier, src[i].tier);
1077 return maxTier;
1080 static void
1081 freeTrackers (tr_tracker_info * trackers, int n)
1083 int i;
1085 for (i=0; i<n; ++i)
1086 tr_free (trackers[i].announce);
1088 tr_free (trackers);
1091 static const char*
1092 addTrackerUrls (tr_torrent * tor, tr_variant * urls)
1094 int i;
1095 int n;
1096 int tier;
1097 tr_variant * val;
1098 tr_tracker_info * trackers;
1099 bool changed = false;
1100 const tr_info * inf = tr_torrentInfo (tor);
1101 const char * errmsg = NULL;
1103 /* make a working copy of the existing announce list */
1104 n = inf->trackerCount;
1105 trackers = tr_new0 (tr_tracker_info, n + tr_variantListSize (urls));
1106 tier = copyTrackers (trackers, inf->trackers, n);
1108 /* and add the new ones */
1109 i = 0;
1110 while ((val = tr_variantListChild (urls, i++)))
1112 const char * announce = NULL;
1114 if ( tr_variantGetStr (val, &announce, NULL)
1115 && tr_urlIsValidTracker (announce)
1116 && !findAnnounceUrl (trackers, n, announce, NULL))
1118 trackers[n].tier = ++tier; /* add a new tier */
1119 trackers[n].announce = tr_strdup (announce);
1120 ++n;
1121 changed = true;
1125 if (!changed)
1126 errmsg = "invalid argument";
1127 else if (!tr_torrentSetAnnounceList (tor, trackers, n))
1128 errmsg = "error setting announce list";
1130 freeTrackers (trackers, n);
1131 return errmsg;
1134 static const char*
1135 replaceTrackers (tr_torrent * tor, tr_variant * urls)
1137 int i;
1138 tr_variant * pair[2];
1139 tr_tracker_info * trackers;
1140 bool changed = false;
1141 const tr_info * inf = tr_torrentInfo (tor);
1142 const int n = inf->trackerCount;
1143 const char * errmsg = NULL;
1145 /* make a working copy of the existing announce list */
1146 trackers = tr_new0 (tr_tracker_info, n);
1147 copyTrackers (trackers, inf->trackers, n);
1149 /* make the substitutions... */
1150 i = 0;
1151 while (((pair[0] = tr_variantListChild (urls,i))) &&
1152 ((pair[1] = tr_variantListChild (urls,i+1))))
1154 size_t len;
1155 int64_t pos;
1156 const char * newval;
1158 if (tr_variantGetInt (pair[0], &pos)
1159 && tr_variantGetStr (pair[1], &newval, &len)
1160 && tr_urlIsValidTracker (newval)
1161 && pos < n
1162 && pos >= 0)
1164 tr_free (trackers[pos].announce);
1165 trackers[pos].announce = tr_strndup (newval, len);
1166 changed = true;
1169 i += 2;
1172 if (!changed)
1173 errmsg = "invalid argument";
1174 else if (!tr_torrentSetAnnounceList (tor, trackers, n))
1175 errmsg = "error setting announce list";
1177 freeTrackers (trackers, n);
1178 return errmsg;
1181 static const char*
1182 removeTrackers (tr_torrent * tor, tr_variant * ids)
1184 int i;
1185 int n;
1186 int t = 0;
1187 int dup = -1;
1188 int * tids;
1189 tr_variant * val;
1190 tr_tracker_info * trackers;
1191 bool changed = false;
1192 const tr_info * inf = tr_torrentInfo (tor);
1193 const char * errmsg = NULL;
1195 /* make a working copy of the existing announce list */
1196 n = inf->trackerCount;
1197 tids = tr_new0 (int, n);
1198 trackers = tr_new0 (tr_tracker_info, n);
1199 copyTrackers (trackers, inf->trackers, n);
1201 /* remove the ones specified in the urls list */
1202 i = 0;
1203 while ((val = tr_variantListChild (ids, i++)))
1205 int64_t pos;
1207 if (tr_variantGetInt (val, &pos) && (0 <= pos) && (pos < n))
1208 tids[t++] = pos;
1211 /* sort trackerIds and remove from largest to smallest so there is no need to recacluate array indicies */
1212 qsort (tids, t, sizeof (int), compareInt);
1213 while (t--)
1215 /* check for duplicates */
1216 if (tids[t] == dup)
1217 continue;
1218 tr_removeElementFromArray (trackers, tids[t], sizeof (tr_tracker_info), n--);
1219 dup = tids[t];
1220 changed = true;
1223 if (!changed)
1224 errmsg = "invalid argument";
1225 else if (!tr_torrentSetAnnounceList (tor, trackers, n))
1226 errmsg = "error setting announce list";
1228 freeTrackers (trackers, n);
1229 tr_free (tids);
1230 return errmsg;
1233 static const char*
1234 torrentSet (tr_session * session,
1235 tr_variant * args_in,
1236 tr_variant * args_out UNUSED,
1237 struct tr_rpc_idle_data * idle_data UNUSED)
1239 int i;
1240 int torrentCount;
1241 tr_torrent ** torrents;
1242 const char * errmsg = NULL;
1244 assert (idle_data == NULL);
1246 torrents = getTorrents (session, args_in, &torrentCount);
1248 for (i=0; i<torrentCount; ++i)
1250 int64_t tmp;
1251 double d;
1252 tr_variant * files;
1253 tr_variant * trackers;
1254 bool boolVal;
1255 tr_torrent * tor;
1257 tor = torrents[i];
1259 if (tr_variantDictFindInt (args_in, TR_KEY_bandwidthPriority, &tmp))
1260 if (tr_isPriority (tmp))
1261 tr_torrentSetPriority (tor, tmp);
1263 if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_files_unwanted, &files))
1264 errmsg = setFileDLs (tor, false, files);
1266 if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_files_wanted, &files))
1267 errmsg = setFileDLs (tor, true, files);
1269 if (tr_variantDictFindInt (args_in, TR_KEY_peer_limit, &tmp))
1270 tr_torrentSetPeerLimit (tor, tmp);
1272 if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_priority_high, &files))
1273 errmsg = setFilePriorities (tor, TR_PRI_HIGH, files);
1275 if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_priority_low, &files))
1276 errmsg = setFilePriorities (tor, TR_PRI_LOW, files);
1278 if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_priority_normal, &files))
1279 errmsg = setFilePriorities (tor, TR_PRI_NORMAL, files);
1281 if (tr_variantDictFindInt (args_in, TR_KEY_downloadLimit, &tmp))
1282 tr_torrentSetSpeedLimit_KBps (tor, TR_DOWN, tmp);
1284 if (tr_variantDictFindBool (args_in, TR_KEY_downloadLimited, &boolVal))
1285 tr_torrentUseSpeedLimit (tor, TR_DOWN, boolVal);
1287 if (tr_variantDictFindBool (args_in, TR_KEY_honorsSessionLimits, &boolVal))
1288 tr_torrentUseSessionLimits (tor, boolVal);
1290 if (tr_variantDictFindInt (args_in, TR_KEY_uploadLimit, &tmp))
1291 tr_torrentSetSpeedLimit_KBps (tor, TR_UP, tmp);
1293 if (tr_variantDictFindBool (args_in, TR_KEY_uploadLimited, &boolVal))
1294 tr_torrentUseSpeedLimit (tor, TR_UP, boolVal);
1296 if (tr_variantDictFindInt (args_in, TR_KEY_seedIdleLimit, &tmp))
1297 tr_torrentSetIdleLimit (tor, tmp);
1299 if (tr_variantDictFindInt (args_in, TR_KEY_seedIdleMode, &tmp))
1300 tr_torrentSetIdleMode (tor, tmp);
1302 if (tr_variantDictFindReal (args_in, TR_KEY_seedRatioLimit, &d))
1303 tr_torrentSetRatioLimit (tor, d);
1305 if (tr_variantDictFindInt (args_in, TR_KEY_seedRatioMode, &tmp))
1306 tr_torrentSetRatioMode (tor, tmp);
1308 if (tr_variantDictFindInt (args_in, TR_KEY_queuePosition, &tmp))
1309 tr_torrentSetQueuePosition (tor, tmp);
1311 if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_trackerAdd, &trackers))
1312 errmsg = addTrackerUrls (tor, trackers);
1314 if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_trackerRemove, &trackers))
1315 errmsg = removeTrackers (tor, trackers);
1317 if (!errmsg && tr_variantDictFindList (args_in, TR_KEY_trackerReplace, &trackers))
1318 errmsg = replaceTrackers (tor, trackers);
1320 notify (session, TR_RPC_TORRENT_CHANGED, tor);
1323 tr_free (torrents);
1324 return errmsg;
1327 static const char*
1328 torrentSetLocation (tr_session * session,
1329 tr_variant * args_in,
1330 tr_variant * args_out UNUSED,
1331 struct tr_rpc_idle_data * idle_data UNUSED)
1333 const char * errmsg = NULL;
1334 const char * location = NULL;
1336 assert (idle_data == NULL);
1338 if (!tr_variantDictFindStr (args_in, TR_KEY_location, &location, NULL))
1340 errmsg = "no location";
1342 else
1344 bool move = false;
1345 int i, torrentCount;
1346 tr_torrent ** torrents = getTorrents (session, args_in, &torrentCount);
1348 tr_variantDictFindBool (args_in, TR_KEY_move, &move);
1350 for (i=0; i<torrentCount; ++i)
1352 tr_torrent * tor = torrents[i];
1353 tr_torrentSetLocation (tor, location, move, NULL, NULL);
1354 notify (session, TR_RPC_TORRENT_MOVED, tor);
1357 tr_free (torrents);
1360 return errmsg;
1363 /***
1364 ****
1365 ***/
1367 static void
1368 torrentRenamePathDone (tr_torrent * tor,
1369 const char * oldpath,
1370 const char * newname,
1371 int error,
1372 void * user_data)
1374 const char * result;
1375 struct tr_rpc_idle_data * data = user_data;
1377 tr_variantDictAddInt (data->args_out, TR_KEY_id, tr_torrentId(tor));
1378 tr_variantDictAddStr (data->args_out, TR_KEY_path, oldpath);
1379 tr_variantDictAddStr (data->args_out, TR_KEY_name, newname);
1381 if (error == 0)
1382 result = NULL;
1383 else
1384 result = tr_strerror (error);
1386 tr_idle_function_done (data, result);
1389 static const char*
1390 torrentRenamePath (tr_session * session,
1391 tr_variant * args_in,
1392 tr_variant * args_out UNUSED,
1393 struct tr_rpc_idle_data * idle_data)
1395 int torrentCount;
1396 tr_torrent ** torrents;
1397 const char * oldpath = NULL;
1398 const char * newname = NULL;
1400 tr_variantDictFindStr (args_in, TR_KEY_path, &oldpath, NULL);
1401 tr_variantDictFindStr (args_in, TR_KEY_name, &newname, NULL);
1402 torrents = getTorrents (session, args_in, &torrentCount);
1404 if (torrentCount == 1)
1405 tr_torrentRenamePath (torrents[0], oldpath, newname, torrentRenamePathDone, idle_data);
1406 else
1407 tr_idle_function_done (idle_data, "torrent-rename-path requires 1 torrent");
1409 /* cleanup */
1410 tr_free (torrents);
1411 return NULL; /* ignored */
1414 /***
1415 ****
1416 ***/
1418 static void
1419 portTested (tr_session * session UNUSED,
1420 bool did_connect UNUSED,
1421 bool did_timeout UNUSED,
1422 long response_code,
1423 const void * response,
1424 size_t response_byte_count,
1425 void * user_data)
1427 char result[1024];
1428 struct tr_rpc_idle_data * data = user_data;
1430 if (response_code != 200)
1432 tr_snprintf (result, sizeof (result), "portTested: http error %ld: %s",
1433 response_code, tr_webGetResponseStr (response_code));
1435 else /* success */
1437 const bool isOpen = response_byte_count && * (char*)response == '1';
1438 tr_variantDictAddBool (data->args_out, TR_KEY_port_is_open, isOpen);
1439 tr_snprintf (result, sizeof (result), "success");
1442 tr_idle_function_done (data, result);
1445 static const char*
1446 portTest (tr_session * session,
1447 tr_variant * args_in UNUSED,
1448 tr_variant * args_out UNUSED,
1449 struct tr_rpc_idle_data * idle_data)
1451 const int port = tr_sessionGetPeerPort (session);
1452 char * url = tr_strdup_printf ("http://portcheck.transmissionbt.com/%d", port);
1453 tr_webRun (session, url, portTested, idle_data);
1454 tr_free (url);
1455 return NULL;
1458 /***
1459 ****
1460 ***/
1462 static void
1463 gotNewBlocklist (tr_session * session,
1464 bool did_connect UNUSED,
1465 bool did_timeout UNUSED,
1466 long response_code,
1467 const void * response,
1468 size_t response_byte_count,
1469 void * user_data)
1471 char result[1024];
1472 struct tr_rpc_idle_data * data = user_data;
1474 *result = '\0';
1476 if (response_code != 200)
1478 tr_snprintf (result, sizeof (result), "gotNewBlocklist: http error %ld: %s",
1479 response_code, tr_webGetResponseStr (response_code));
1481 else /* successfully fetched the blocklist... */
1483 int fd;
1484 int err;
1485 char * filename;
1486 z_stream stream;
1487 const char * configDir = tr_sessionGetConfigDir (session);
1488 const size_t buflen = 1024 * 128; /* 128 KiB buffer */
1489 uint8_t * buf = tr_valloc (buflen);
1491 /* this is an odd Magic Number required by zlib to enable gz support.
1492 See zlib's inflateInit2 () documentation for a full description */
1493 const int windowBits = 15 + 32;
1495 stream.zalloc = (alloc_func) Z_NULL;
1496 stream.zfree = (free_func) Z_NULL;
1497 stream.opaque = (voidpf) Z_NULL;
1498 stream.next_in = (void*) response;
1499 stream.avail_in = response_byte_count;
1500 inflateInit2 (&stream, windowBits);
1502 filename = tr_buildPath (configDir, "blocklist.tmp", NULL);
1503 fd = tr_open_file_for_writing (filename);
1504 if (fd < 0)
1505 tr_snprintf (result, sizeof (result), _("Couldn't save file \"%1$s\": %2$s"), filename, tr_strerror (errno));
1507 for (;;)
1509 stream.next_out = (void*) buf;
1510 stream.avail_out = buflen;
1511 err = inflate (&stream, Z_NO_FLUSH);
1513 if (stream.avail_out < buflen)
1515 const int e = write (fd, buf, buflen - stream.avail_out);
1516 if (e < 0)
1518 tr_snprintf (result, sizeof (result), _("Couldn't save file \"%1$s\": %2$s"), filename, tr_strerror (errno));
1519 break;
1523 if (err != Z_OK)
1525 if ((err != Z_STREAM_END) && (err != Z_DATA_ERROR))
1526 tr_snprintf (result, sizeof (result), _("Error uncompressing blocklist: %s (%d)"), zError (err), err);
1527 break;
1531 inflateEnd (&stream);
1533 if (err == Z_DATA_ERROR) /* couldn't inflate it... it's probably already uncompressed */
1534 if (write (fd, response, response_byte_count) < 0)
1535 tr_snprintf (result, sizeof (result), _("Couldn't save file \"%1$s\": %2$s"), filename, tr_strerror (errno));
1537 if (*result)
1539 tr_logAddError ("%s", result);
1541 else
1543 /* feed it to the session and give the client a response */
1544 const int rule_count = tr_blocklistSetContent (session, filename);
1545 tr_variantDictAddInt (data->args_out, TR_KEY_blocklist_size, rule_count);
1546 tr_snprintf (result, sizeof (result), "success");
1549 tr_remove (filename);
1550 tr_free (filename);
1551 tr_free (buf);
1554 tr_idle_function_done (data, result);
1557 static const char*
1558 blocklistUpdate (tr_session * session,
1559 tr_variant * args_in UNUSED,
1560 tr_variant * args_out UNUSED,
1561 struct tr_rpc_idle_data * idle_data)
1563 tr_webRun (session, session->blocklist_url, gotNewBlocklist, idle_data);
1564 return NULL;
1567 /***
1568 ****
1569 ***/
1571 static void
1572 addTorrentImpl (struct tr_rpc_idle_data * data, tr_ctor * ctor)
1574 int err;
1575 int duplicate_id;
1576 const char * result;
1577 tr_torrent * tor;
1578 tr_quark key;
1580 err = 0;
1581 duplicate_id = 0;
1582 tor = tr_torrentNew (ctor, &err, &duplicate_id);
1583 tr_ctorFree (ctor);
1585 if (!err)
1587 key = TR_KEY_torrent_added;
1588 result = NULL;
1590 else if (err == TR_PARSE_ERR)
1592 key = 0;
1593 result = "invalid or corrupt torrent file";
1595 else if (err == TR_PARSE_DUPLICATE)
1597 tor = tr_torrentFindFromId (data->session, duplicate_id);
1598 key = TR_KEY_torrent_duplicate;
1599 result = "duplicate torrent";
1602 if (tor && key)
1604 tr_variant fields;
1605 tr_variantInitList (&fields, 3);
1606 tr_variantListAddStr (&fields, "id");
1607 tr_variantListAddStr (&fields, "name");
1608 tr_variantListAddStr (&fields, "hashString");
1609 addInfo (tor, tr_variantDictAdd (data->args_out, key), &fields);
1610 notify (data->session, TR_RPC_TORRENT_ADDED, tor);
1611 tr_variantFree (&fields);
1612 result = NULL;
1615 tr_idle_function_done (data, result);
1619 struct add_torrent_idle_data
1621 struct tr_rpc_idle_data * data;
1622 tr_ctor * ctor;
1625 static void
1626 gotMetadataFromURL (tr_session * session UNUSED,
1627 bool did_connect UNUSED,
1628 bool did_timeout UNUSED,
1629 long response_code,
1630 const void * response,
1631 size_t response_byte_count,
1632 void * user_data)
1634 struct add_torrent_idle_data * data = user_data;
1636 dbgmsg ("torrentAdd: HTTP response code was %ld (%s); response length was %zu bytes",
1637 response_code, tr_webGetResponseStr (response_code), response_byte_count);
1639 if (response_code==200 || response_code==221) /* http or ftp success.. */
1641 tr_ctorSetMetainfo (data->ctor, response, response_byte_count);
1642 addTorrentImpl (data->data, data->ctor);
1644 else
1646 char result[1024];
1647 tr_snprintf (result, sizeof (result), "gotMetadataFromURL: http error %ld: %s",
1648 response_code, tr_webGetResponseStr (response_code));
1649 tr_idle_function_done (data->data, result);
1652 tr_free (data);
1655 static bool
1656 isCurlURL (const char * filename)
1658 if (filename == NULL)
1659 return false;
1661 return !strncmp (filename, "ftp://", 6) ||
1662 !strncmp (filename, "http://", 7) ||
1663 !strncmp (filename, "https://", 8);
1666 static tr_file_index_t*
1667 fileListFromList (tr_variant * list, tr_file_index_t * setmeCount)
1669 size_t i;
1670 const size_t childCount = tr_variantListSize (list);
1671 tr_file_index_t n = 0;
1672 tr_file_index_t * files = tr_new0 (tr_file_index_t, childCount);
1674 for (i=0; i<childCount; ++i)
1676 int64_t intVal;
1677 if (tr_variantGetInt (tr_variantListChild (list, i), &intVal))
1678 files[n++] = (tr_file_index_t)intVal;
1681 *setmeCount = n;
1682 return files;
1685 static const char*
1686 torrentAdd (tr_session * session,
1687 tr_variant * args_in,
1688 tr_variant * args_out UNUSED,
1689 struct tr_rpc_idle_data * idle_data)
1691 const char * filename = NULL;
1692 const char * metainfo_base64 = NULL;
1694 assert (idle_data != NULL);
1696 tr_variantDictFindStr (args_in, TR_KEY_filename, &filename, NULL);
1697 tr_variantDictFindStr (args_in, TR_KEY_metainfo, &metainfo_base64, NULL);
1698 if (!filename && !metainfo_base64)
1700 return "no filename or metainfo specified";
1702 else
1704 int64_t i;
1705 bool boolVal;
1706 tr_variant * l;
1707 const char * str;
1708 const char * cookies = NULL;
1709 tr_ctor * ctor = tr_ctorNew (session);
1711 /* set the optional arguments */
1713 tr_variantDictFindStr (args_in, TR_KEY_cookies, &cookies, NULL);
1715 if (tr_variantDictFindStr (args_in, TR_KEY_download_dir, &str, NULL))
1716 tr_ctorSetDownloadDir (ctor, TR_FORCE, str);
1718 if (tr_variantDictFindBool (args_in, TR_KEY_paused, &boolVal))
1719 tr_ctorSetPaused (ctor, TR_FORCE, boolVal);
1721 if (tr_variantDictFindInt (args_in, TR_KEY_peer_limit, &i))
1722 tr_ctorSetPeerLimit (ctor, TR_FORCE, i);
1724 if (tr_variantDictFindInt (args_in, TR_KEY_bandwidthPriority, &i))
1725 tr_ctorSetBandwidthPriority (ctor, i);
1727 if (tr_variantDictFindList (args_in, TR_KEY_files_unwanted, &l))
1729 tr_file_index_t fileCount;
1730 tr_file_index_t * files = fileListFromList (l, &fileCount);
1731 tr_ctorSetFilesWanted (ctor, files, fileCount, false);
1732 tr_free (files);
1735 if (tr_variantDictFindList (args_in, TR_KEY_files_wanted, &l))
1737 tr_file_index_t fileCount;
1738 tr_file_index_t * files = fileListFromList (l, &fileCount);
1739 tr_ctorSetFilesWanted (ctor, files, fileCount, true);
1740 tr_free (files);
1743 if (tr_variantDictFindList (args_in, TR_KEY_priority_low, &l))
1745 tr_file_index_t fileCount;
1746 tr_file_index_t * files = fileListFromList (l, &fileCount);
1747 tr_ctorSetFilePriorities (ctor, files, fileCount, TR_PRI_LOW);
1748 tr_free (files);
1751 if (tr_variantDictFindList (args_in, TR_KEY_priority_normal, &l))
1753 tr_file_index_t fileCount;
1754 tr_file_index_t * files = fileListFromList (l, &fileCount);
1755 tr_ctorSetFilePriorities (ctor, files, fileCount, TR_PRI_NORMAL);
1756 tr_free (files);
1759 if (tr_variantDictFindList (args_in, TR_KEY_priority_high, &l))
1761 tr_file_index_t fileCount;
1762 tr_file_index_t * files = fileListFromList (l, &fileCount);
1763 tr_ctorSetFilePriorities (ctor, files, fileCount, TR_PRI_HIGH);
1764 tr_free (files);
1767 dbgmsg ("torrentAdd: filename is \"%s\"", filename ? filename : " (null)");
1769 if (isCurlURL (filename))
1771 struct add_torrent_idle_data * d = tr_new0 (struct add_torrent_idle_data, 1);
1772 d->data = idle_data;
1773 d->ctor = ctor;
1774 tr_webRunWithCookies (session, filename, cookies, gotMetadataFromURL, d);
1776 else
1778 char * fname = tr_strstrip (tr_strdup (filename));
1780 if (fname == NULL)
1782 int len;
1783 char * metainfo = tr_base64_decode (metainfo_base64, -1, &len);
1784 tr_ctorSetMetainfo (ctor, (uint8_t*)metainfo, len);
1785 tr_free (metainfo);
1787 else if (!strncmp (fname, "magnet:?", 8))
1789 tr_ctorSetMetainfoFromMagnetLink (ctor, fname);
1791 else
1793 tr_ctorSetMetainfoFromFile (ctor, fname);
1796 addTorrentImpl (idle_data, ctor);
1798 tr_free (fname);
1803 return NULL;
1806 /***
1807 ****
1808 ***/
1810 static const char*
1811 sessionSet (tr_session * session,
1812 tr_variant * args_in,
1813 tr_variant * args_out UNUSED,
1814 struct tr_rpc_idle_data * idle_data UNUSED)
1816 int64_t i;
1817 double d;
1818 bool boolVal;
1819 const char * str;
1821 assert (idle_data == NULL);
1823 if (tr_variantDictFindInt (args_in, TR_KEY_cache_size_mb, &i))
1824 tr_sessionSetCacheLimit_MB (session, i);
1826 if (tr_variantDictFindInt (args_in, TR_KEY_alt_speed_up, &i))
1827 tr_sessionSetAltSpeed_KBps (session, TR_UP, i);
1829 if (tr_variantDictFindInt (args_in, TR_KEY_alt_speed_down, &i))
1830 tr_sessionSetAltSpeed_KBps (session, TR_DOWN, i);
1832 if (tr_variantDictFindBool (args_in, TR_KEY_alt_speed_enabled, &boolVal))
1833 tr_sessionUseAltSpeed (session, boolVal);
1835 if (tr_variantDictFindInt (args_in, TR_KEY_alt_speed_time_begin, &i))
1836 tr_sessionSetAltSpeedBegin (session, i);
1838 if (tr_variantDictFindInt (args_in, TR_KEY_alt_speed_time_end, &i))
1839 tr_sessionSetAltSpeedEnd (session, i);
1841 if (tr_variantDictFindInt (args_in, TR_KEY_alt_speed_time_day, &i))
1842 tr_sessionSetAltSpeedDay (session, i);
1844 if (tr_variantDictFindBool (args_in, TR_KEY_alt_speed_time_enabled, &boolVal))
1845 tr_sessionUseAltSpeedTime (session, boolVal);
1847 if (tr_variantDictFindBool (args_in, TR_KEY_blocklist_enabled, &boolVal))
1848 tr_blocklistSetEnabled (session, boolVal);
1850 if (tr_variantDictFindStr (args_in, TR_KEY_blocklist_url, &str, NULL))
1851 tr_blocklistSetURL (session, str);
1853 if (tr_variantDictFindStr (args_in, TR_KEY_download_dir, &str, NULL))
1854 tr_sessionSetDownloadDir (session, str);
1856 if (tr_variantDictFindInt (args_in, TR_KEY_queue_stalled_minutes, &i))
1857 tr_sessionSetQueueStalledMinutes (session, i);
1859 if (tr_variantDictFindBool (args_in, TR_KEY_queue_stalled_enabled, &boolVal))
1860 tr_sessionSetQueueStalledEnabled (session, boolVal);
1862 if (tr_variantDictFindInt (args_in, TR_KEY_download_queue_size, &i))
1863 tr_sessionSetQueueSize (session, TR_DOWN, i);
1865 if (tr_variantDictFindBool (args_in, TR_KEY_download_queue_enabled, &boolVal))
1866 tr_sessionSetQueueEnabled (session, TR_DOWN, boolVal);
1868 if (tr_variantDictFindStr (args_in, TR_KEY_incomplete_dir, &str, NULL))
1869 tr_sessionSetIncompleteDir (session, str);
1871 if (tr_variantDictFindBool (args_in, TR_KEY_incomplete_dir_enabled, &boolVal))
1872 tr_sessionSetIncompleteDirEnabled (session, boolVal);
1874 if (tr_variantDictFindInt (args_in, TR_KEY_peer_limit_global, &i))
1875 tr_sessionSetPeerLimit (session, i);
1877 if (tr_variantDictFindInt (args_in, TR_KEY_peer_limit_per_torrent, &i))
1878 tr_sessionSetPeerLimitPerTorrent (session, i);
1880 if (tr_variantDictFindBool (args_in, TR_KEY_pex_enabled, &boolVal))
1881 tr_sessionSetPexEnabled (session, boolVal);
1883 if (tr_variantDictFindBool (args_in, TR_KEY_dht_enabled, &boolVal))
1884 tr_sessionSetDHTEnabled (session, boolVal);
1886 if (tr_variantDictFindBool (args_in, TR_KEY_utp_enabled, &boolVal))
1887 tr_sessionSetUTPEnabled (session, boolVal);
1889 if (tr_variantDictFindBool (args_in, TR_KEY_lpd_enabled, &boolVal))
1890 tr_sessionSetLPDEnabled (session, boolVal);
1892 if (tr_variantDictFindBool (args_in, TR_KEY_peer_port_random_on_start, &boolVal))
1893 tr_sessionSetPeerPortRandomOnStart (session, boolVal);
1895 if (tr_variantDictFindInt (args_in, TR_KEY_peer_port, &i))
1896 tr_sessionSetPeerPort (session, i);
1898 if (tr_variantDictFindBool (args_in, TR_KEY_port_forwarding_enabled, &boolVal))
1899 tr_sessionSetPortForwardingEnabled (session, boolVal);
1901 if (tr_variantDictFindBool (args_in, TR_KEY_rename_partial_files, &boolVal))
1902 tr_sessionSetIncompleteFileNamingEnabled (session, boolVal);
1904 if (tr_variantDictFindReal (args_in, TR_KEY_seedRatioLimit, &d))
1905 tr_sessionSetRatioLimit (session, d);
1907 if (tr_variantDictFindBool (args_in, TR_KEY_seedRatioLimited, &boolVal))
1908 tr_sessionSetRatioLimited (session, boolVal);
1910 if (tr_variantDictFindInt (args_in, TR_KEY_idle_seeding_limit, &i))
1911 tr_sessionSetIdleLimit (session, i);
1913 if (tr_variantDictFindBool (args_in, TR_KEY_idle_seeding_limit_enabled, &boolVal))
1914 tr_sessionSetIdleLimited (session, boolVal);
1916 if (tr_variantDictFindBool (args_in, TR_KEY_start_added_torrents, &boolVal))
1917 tr_sessionSetPaused (session, !boolVal);
1919 if (tr_variantDictFindBool (args_in, TR_KEY_seed_queue_enabled, &boolVal))
1920 tr_sessionSetQueueEnabled (session, TR_UP, boolVal);
1922 if (tr_variantDictFindInt (args_in, TR_KEY_seed_queue_size, &i))
1923 tr_sessionSetQueueSize (session, TR_UP, i);
1925 if (tr_variantDictFindStr (args_in, TR_KEY_script_torrent_done_filename, &str, NULL))
1926 tr_sessionSetTorrentDoneScript (session, str);
1928 if (tr_variantDictFindBool (args_in, TR_KEY_script_torrent_done_enabled, &boolVal))
1929 tr_sessionSetTorrentDoneScriptEnabled (session, boolVal);
1931 if (tr_variantDictFindBool (args_in, TR_KEY_trash_original_torrent_files, &boolVal))
1932 tr_sessionSetDeleteSource (session, boolVal);
1934 if (tr_variantDictFindInt (args_in, TR_KEY_speed_limit_down, &i))
1935 tr_sessionSetSpeedLimit_KBps (session, TR_DOWN, i);
1937 if (tr_variantDictFindBool (args_in, TR_KEY_speed_limit_down_enabled, &boolVal))
1938 tr_sessionLimitSpeed (session, TR_DOWN, boolVal);
1940 if (tr_variantDictFindInt (args_in, TR_KEY_speed_limit_up, &i))
1941 tr_sessionSetSpeedLimit_KBps (session, TR_UP, i);
1943 if (tr_variantDictFindBool (args_in, TR_KEY_speed_limit_up_enabled, &boolVal))
1944 tr_sessionLimitSpeed (session, TR_UP, boolVal);
1946 if (tr_variantDictFindStr (args_in, TR_KEY_encryption, &str, NULL))
1948 if (!tr_strcmp0 (str, "required"))
1949 tr_sessionSetEncryption (session, TR_ENCRYPTION_REQUIRED);
1950 else if (!tr_strcmp0 (str, "tolerated"))
1951 tr_sessionSetEncryption (session, TR_CLEAR_PREFERRED);
1952 else
1953 tr_sessionSetEncryption (session, TR_ENCRYPTION_PREFERRED);
1956 notify (session, TR_RPC_SESSION_CHANGED, NULL);
1958 return NULL;
1961 static const char*
1962 sessionStats (tr_session * session,
1963 tr_variant * args_in UNUSED,
1964 tr_variant * args_out,
1965 struct tr_rpc_idle_data * idle_data UNUSED)
1967 int running = 0;
1968 int total = 0;
1969 tr_variant * d;
1970 tr_session_stats currentStats = { 0.0f, 0, 0, 0, 0, 0 };
1971 tr_session_stats cumulativeStats = { 0.0f, 0, 0, 0, 0, 0 };
1972 tr_torrent * tor = NULL;
1974 assert (idle_data == NULL);
1976 while ((tor = tr_torrentNext (session, tor)))
1978 ++total;
1980 if (tor->isRunning)
1981 ++running;
1984 tr_sessionGetStats (session, &currentStats);
1985 tr_sessionGetCumulativeStats (session, &cumulativeStats);
1987 tr_variantDictAddInt (args_out, TR_KEY_activeTorrentCount, running);
1988 tr_variantDictAddReal (args_out, TR_KEY_downloadSpeed, tr_sessionGetPieceSpeed_Bps (session, TR_DOWN));
1989 tr_variantDictAddInt (args_out, TR_KEY_pausedTorrentCount, total - running);
1990 tr_variantDictAddInt (args_out, TR_KEY_torrentCount, total);
1991 tr_variantDictAddReal (args_out, TR_KEY_uploadSpeed, tr_sessionGetPieceSpeed_Bps (session, TR_UP));
1993 d = tr_variantDictAddDict (args_out, TR_KEY_cumulative_stats, 5);
1994 tr_variantDictAddInt (d, TR_KEY_downloadedBytes, cumulativeStats.downloadedBytes);
1995 tr_variantDictAddInt (d, TR_KEY_filesAdded, cumulativeStats.filesAdded);
1996 tr_variantDictAddInt (d, TR_KEY_secondsActive, cumulativeStats.secondsActive);
1997 tr_variantDictAddInt (d, TR_KEY_sessionCount, cumulativeStats.sessionCount);
1998 tr_variantDictAddInt (d, TR_KEY_uploadedBytes, cumulativeStats.uploadedBytes);
2000 d = tr_variantDictAddDict (args_out, TR_KEY_current_stats, 5);
2001 tr_variantDictAddInt (d, TR_KEY_downloadedBytes, currentStats.downloadedBytes);
2002 tr_variantDictAddInt (d, TR_KEY_filesAdded, currentStats.filesAdded);
2003 tr_variantDictAddInt (d, TR_KEY_secondsActive, currentStats.secondsActive);
2004 tr_variantDictAddInt (d, TR_KEY_sessionCount, currentStats.sessionCount);
2005 tr_variantDictAddInt (d, TR_KEY_uploadedBytes, currentStats.uploadedBytes);
2007 return NULL;
2010 static const char*
2011 sessionGet (tr_session * s,
2012 tr_variant * args_in UNUSED,
2013 tr_variant * args_out,
2014 struct tr_rpc_idle_data * idle_data UNUSED)
2016 const char * str;
2017 tr_variant * d = args_out;
2019 assert (idle_data == NULL);
2020 tr_variantDictAddInt (d, TR_KEY_alt_speed_up, tr_sessionGetAltSpeed_KBps (s,TR_UP));
2021 tr_variantDictAddInt (d, TR_KEY_alt_speed_down, tr_sessionGetAltSpeed_KBps (s,TR_DOWN));
2022 tr_variantDictAddBool (d, TR_KEY_alt_speed_enabled, tr_sessionUsesAltSpeed (s));
2023 tr_variantDictAddInt (d, TR_KEY_alt_speed_time_begin, tr_sessionGetAltSpeedBegin (s));
2024 tr_variantDictAddInt (d, TR_KEY_alt_speed_time_end,tr_sessionGetAltSpeedEnd (s));
2025 tr_variantDictAddInt (d, TR_KEY_alt_speed_time_day,tr_sessionGetAltSpeedDay (s));
2026 tr_variantDictAddBool (d, TR_KEY_alt_speed_time_enabled, tr_sessionUsesAltSpeedTime (s));
2027 tr_variantDictAddBool (d, TR_KEY_blocklist_enabled, tr_blocklistIsEnabled (s));
2028 tr_variantDictAddStr (d, TR_KEY_blocklist_url, tr_blocklistGetURL (s));
2029 tr_variantDictAddInt (d, TR_KEY_cache_size_mb, tr_sessionGetCacheLimit_MB (s));
2030 tr_variantDictAddInt (d, TR_KEY_blocklist_size, tr_blocklistGetRuleCount (s));
2031 tr_variantDictAddStr (d, TR_KEY_config_dir, tr_sessionGetConfigDir (s));
2032 tr_variantDictAddStr (d, TR_KEY_download_dir, tr_sessionGetDownloadDir (s));
2033 tr_variantDictAddInt (d, TR_KEY_download_dir_free_space, tr_device_info_get_free_space (s->downloadDir));
2034 tr_variantDictAddBool (d, TR_KEY_download_queue_enabled, tr_sessionGetQueueEnabled (s, TR_DOWN));
2035 tr_variantDictAddInt (d, TR_KEY_download_queue_size, tr_sessionGetQueueSize (s, TR_DOWN));
2036 tr_variantDictAddInt (d, TR_KEY_peer_limit_global, tr_sessionGetPeerLimit (s));
2037 tr_variantDictAddInt (d, TR_KEY_peer_limit_per_torrent, tr_sessionGetPeerLimitPerTorrent (s));
2038 tr_variantDictAddStr (d, TR_KEY_incomplete_dir, tr_sessionGetIncompleteDir (s));
2039 tr_variantDictAddBool (d, TR_KEY_incomplete_dir_enabled, tr_sessionIsIncompleteDirEnabled (s));
2040 tr_variantDictAddBool (d, TR_KEY_pex_enabled, tr_sessionIsPexEnabled (s));
2041 tr_variantDictAddBool (d, TR_KEY_utp_enabled, tr_sessionIsUTPEnabled (s));
2042 tr_variantDictAddBool (d, TR_KEY_dht_enabled, tr_sessionIsDHTEnabled (s));
2043 tr_variantDictAddBool (d, TR_KEY_lpd_enabled, tr_sessionIsLPDEnabled (s));
2044 tr_variantDictAddInt (d, TR_KEY_peer_port, tr_sessionGetPeerPort (s));
2045 tr_variantDictAddBool (d, TR_KEY_peer_port_random_on_start, tr_sessionGetPeerPortRandomOnStart (s));
2046 tr_variantDictAddBool (d, TR_KEY_port_forwarding_enabled, tr_sessionIsPortForwardingEnabled (s));
2047 tr_variantDictAddBool (d, TR_KEY_rename_partial_files, tr_sessionIsIncompleteFileNamingEnabled (s));
2048 tr_variantDictAddInt (d, TR_KEY_rpc_version, RPC_VERSION);
2049 tr_variantDictAddInt (d, TR_KEY_rpc_version_minimum, RPC_VERSION_MIN);
2050 tr_variantDictAddReal (d, TR_KEY_seedRatioLimit, tr_sessionGetRatioLimit (s));
2051 tr_variantDictAddBool (d, TR_KEY_seedRatioLimited, tr_sessionIsRatioLimited (s));
2052 tr_variantDictAddInt (d, TR_KEY_idle_seeding_limit, tr_sessionGetIdleLimit (s));
2053 tr_variantDictAddBool (d, TR_KEY_idle_seeding_limit_enabled, tr_sessionIsIdleLimited (s));
2054 tr_variantDictAddBool (d, TR_KEY_seed_queue_enabled, tr_sessionGetQueueEnabled (s, TR_UP));
2055 tr_variantDictAddInt (d, TR_KEY_seed_queue_size, tr_sessionGetQueueSize (s, TR_UP));
2056 tr_variantDictAddBool (d, TR_KEY_start_added_torrents, !tr_sessionGetPaused (s));
2057 tr_variantDictAddBool (d, TR_KEY_trash_original_torrent_files, tr_sessionGetDeleteSource (s));
2058 tr_variantDictAddInt (d, TR_KEY_speed_limit_up, tr_sessionGetSpeedLimit_KBps (s, TR_UP));
2059 tr_variantDictAddBool (d, TR_KEY_speed_limit_up_enabled, tr_sessionIsSpeedLimited (s, TR_UP));
2060 tr_variantDictAddInt (d, TR_KEY_speed_limit_down, tr_sessionGetSpeedLimit_KBps (s, TR_DOWN));
2061 tr_variantDictAddBool (d, TR_KEY_speed_limit_down_enabled, tr_sessionIsSpeedLimited (s, TR_DOWN));
2062 tr_variantDictAddStr (d, TR_KEY_script_torrent_done_filename, tr_sessionGetTorrentDoneScript (s));
2063 tr_variantDictAddBool (d, TR_KEY_script_torrent_done_enabled, tr_sessionIsTorrentDoneScriptEnabled (s));
2064 tr_variantDictAddBool (d, TR_KEY_queue_stalled_enabled, tr_sessionGetQueueStalledEnabled (s));
2065 tr_variantDictAddInt (d, TR_KEY_queue_stalled_minutes, tr_sessionGetQueueStalledMinutes (s));
2066 tr_formatter_get_units (tr_variantDictAddDict (d, TR_KEY_units, 0));
2067 tr_variantDictAddStr (d, TR_KEY_version, LONG_VERSION_STRING);
2068 switch (tr_sessionGetEncryption (s))
2070 case TR_CLEAR_PREFERRED: str = "tolerated"; break;
2071 case TR_ENCRYPTION_REQUIRED: str = "required"; break;
2072 default: str = "preferred"; break;
2074 tr_variantDictAddStr (d, TR_KEY_encryption, str);
2076 return NULL;
2079 static const char*
2080 freeSpace (tr_session * session,
2081 tr_variant * args_in,
2082 tr_variant * args_out,
2083 struct tr_rpc_idle_data * idle_data UNUSED)
2085 int tmperr;
2086 const char * path = NULL;
2087 const char * err = NULL;
2088 int64_t free_space = -1;
2090 /* get the free space */
2091 tr_variantDictFindStr (args_in, TR_KEY_path, &path, NULL);
2092 tmperr = errno;
2093 errno = 0;
2094 free_space = tr_sessionGetDirFreeSpace (session, path);
2095 if (free_space < 0)
2096 err = tr_strerror (errno);
2097 errno = tmperr;
2099 /* response */
2100 if (path != NULL)
2101 tr_variantDictAddStr (args_out, TR_KEY_path, path);
2102 tr_variantDictAddInt (args_out, TR_KEY_size_bytes, free_space);
2103 return err;
2106 /***
2107 ****
2108 ***/
2110 static const char*
2111 sessionClose (tr_session * session,
2112 tr_variant * args_in UNUSED,
2113 tr_variant * args_out UNUSED,
2114 struct tr_rpc_idle_data * idle_data UNUSED)
2116 notify (session, TR_RPC_SESSION_CLOSE, NULL);
2117 return NULL;
2120 /***
2121 ****
2122 ***/
2124 typedef const char* (*handler)(tr_session*, tr_variant*, tr_variant*, struct tr_rpc_idle_data *);
2126 static struct method
2128 const char * name;
2129 bool immediate;
2130 handler func;
2132 methods[] =
2134 { "port-test", false, portTest },
2135 { "blocklist-update", false, blocklistUpdate },
2136 { "free-space", true, freeSpace },
2137 { "session-close", true, sessionClose },
2138 { "session-get", true, sessionGet },
2139 { "session-set", true, sessionSet },
2140 { "session-stats", true, sessionStats },
2141 { "torrent-add", false, torrentAdd },
2142 { "torrent-get", true, torrentGet },
2143 { "torrent-remove", true, torrentRemove },
2144 { "torrent-rename-path", false, torrentRenamePath },
2145 { "torrent-set", true, torrentSet },
2146 { "torrent-set-location", true, torrentSetLocation },
2147 { "torrent-start", true, torrentStart },
2148 { "torrent-start-now", true, torrentStartNow },
2149 { "torrent-stop", true, torrentStop },
2150 { "torrent-verify", true, torrentVerify },
2151 { "torrent-reannounce", true, torrentReannounce },
2152 { "queue-move-top", true, queueMoveTop },
2153 { "queue-move-up", true, queueMoveUp },
2154 { "queue-move-down", true, queueMoveDown },
2155 { "queue-move-bottom", true, queueMoveBottom }
2158 static void
2159 noop_response_callback (tr_session * session UNUSED,
2160 struct evbuffer * response UNUSED,
2161 void * user_data UNUSED)
2165 static void
2166 request_exec (tr_session * session,
2167 tr_variant * request,
2168 tr_rpc_response_func callback,
2169 void * callback_user_data)
2171 int i;
2172 const char * str;
2173 tr_variant * args_in = tr_variantDictFind (request, TR_KEY_arguments);
2174 const char * result = NULL;
2176 if (callback == NULL)
2177 callback = noop_response_callback;
2179 /* parse the request */
2180 if (!tr_variantDictFindStr (request, TR_KEY_method, &str, NULL))
2182 result = "no method name";
2184 else
2186 const int n = TR_N_ELEMENTS (methods);
2188 for (i=0; i<n; ++i)
2189 if (!strcmp (str, methods[i].name))
2190 break;
2192 if (i ==n)
2193 result = "method name not recognized";
2196 /* if we couldn't figure out which method to use, return an error */
2197 if (result != NULL)
2199 int64_t tag;
2200 tr_variant response;
2201 struct evbuffer * buf;
2203 tr_variantInitDict (&response, 3);
2204 tr_variantDictAddDict (&response, TR_KEY_arguments, 0);
2205 tr_variantDictAddStr (&response, TR_KEY_result, result);
2206 if (tr_variantDictFindInt (request, TR_KEY_tag, &tag))
2207 tr_variantDictAddInt (&response, TR_KEY_tag, tag);
2209 buf = tr_variantToBuf (&response, TR_VARIANT_FMT_JSON_LEAN);
2210 (*callback)(session, buf, callback_user_data);
2211 evbuffer_free (buf);
2213 tr_variantFree (&response);
2215 else if (methods[i].immediate)
2217 int64_t tag;
2218 tr_variant response;
2219 tr_variant * args_out;
2220 struct evbuffer * buf;
2222 tr_variantInitDict (&response, 3);
2223 args_out = tr_variantDictAddDict (&response, TR_KEY_arguments, 0);
2224 result = (*methods[i].func)(session, args_in, args_out, NULL);
2225 if (result == NULL)
2226 result = "success";
2227 tr_variantDictAddStr (&response, TR_KEY_result, result);
2228 if (tr_variantDictFindInt (request, TR_KEY_tag, &tag))
2229 tr_variantDictAddInt (&response, TR_KEY_tag, tag);
2231 buf = tr_variantToBuf (&response, TR_VARIANT_FMT_JSON_LEAN);
2232 (*callback)(session, buf, callback_user_data);
2233 evbuffer_free (buf);
2235 tr_variantFree (&response);
2237 else
2239 int64_t tag;
2240 struct tr_rpc_idle_data * data = tr_new0 (struct tr_rpc_idle_data, 1);
2241 data->session = session;
2242 data->response = tr_new0 (tr_variant, 1);
2243 tr_variantInitDict (data->response, 3);
2244 if (tr_variantDictFindInt (request, TR_KEY_tag, &tag))
2245 tr_variantDictAddInt (data->response, TR_KEY_tag, tag);
2246 data->args_out = tr_variantDictAddDict (data->response, TR_KEY_arguments, 0);
2247 data->callback = callback;
2248 data->callback_user_data = callback_user_data;
2249 (*methods[i].func)(session, args_in, data->args_out, data);
2253 void
2254 tr_rpc_request_exec_json (tr_session * session,
2255 const void * request_json,
2256 int request_len,
2257 tr_rpc_response_func callback,
2258 void * callback_user_data)
2260 tr_variant top;
2261 int have_content;
2263 if (request_len < 0)
2264 request_len = strlen (request_json);
2266 have_content = !tr_variantFromJson (&top, request_json, request_len);
2267 request_exec (session, have_content ? &top : NULL, callback, callback_user_data);
2269 if (have_content)
2270 tr_variantFree (&top);
2274 * Munge the URI into a usable form.
2276 * We have very loose typing on this to make the URIs as simple as possible:
2277 * - anything not a 'tag' or 'method' is automatically in 'arguments'
2278 * - values that are all-digits are numbers
2279 * - values that are all-digits or commas are number lists
2280 * - all other values are strings
2282 void
2283 tr_rpc_parse_list_str (tr_variant * setme,
2284 const char * str,
2285 int len)
2288 int valueCount;
2289 int * values = tr_parseNumberRange (str, len, &valueCount);
2291 if (valueCount == 0)
2293 tr_variantInitStr (setme, str, len);
2295 else if (valueCount == 1)
2297 tr_variantInitInt (setme, values[0]);
2299 else
2301 int i;
2303 tr_variantInitList (setme, valueCount);
2305 for (i=0; i<valueCount; ++i)
2306 tr_variantListAddInt (setme, values[i]);
2309 tr_free (values);
2312 void
2313 tr_rpc_request_exec_uri (tr_session * session,
2314 const void * request_uri,
2315 int request_len,
2316 tr_rpc_response_func callback,
2317 void * callback_user_data)
2319 const char * pch;
2320 tr_variant top;
2321 tr_variant * args;
2322 char * request = tr_strndup (request_uri, request_len);
2324 tr_variantInitDict (&top, 3);
2325 args = tr_variantDictAddDict (&top, TR_KEY_arguments, 0);
2327 pch = strchr (request, '?');
2328 if (!pch) pch = request;
2329 while (pch)
2331 const char * delim = strchr (pch, '=');
2332 const char * next = strchr (pch, '&');
2333 if (delim)
2335 char * key = tr_strndup (pch, delim - pch);
2336 int isArg = strcmp (key, "method") && strcmp (key, "tag");
2337 tr_variant * parent = isArg ? args : &top;
2339 tr_rpc_parse_list_str (tr_variantDictAdd (parent, tr_quark_new (key, delim-pch)),
2340 delim + 1,
2341 next ? (size_t)(next - (delim + 1)) : strlen (delim + 1));
2342 tr_free (key);
2345 pch = next ? next + 1 : NULL;
2348 request_exec (session, &top, callback, callback_user_data);
2350 /* cleanup */
2351 tr_variantFree (&top);
2352 tr_free (request);