2 * This file Copyright (C) 2008-2014 Mnemosyne LLC
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
7 * $Id: remote.c 14263 2014-04-27 19:57:38Z jordan $
11 #include <ctype.h> /* isspace */
16 #include <string.h> /* strcmp */
19 #include <direct.h> /* getcwd */
21 #include <unistd.h> /* getcwd */
24 #include <event2/buffer.h>
26 #define CURL_DISABLE_TYPECHECK /* otherwise -Wunreachable-code goes insane */
27 #include <curl/curl.h>
29 #include <libtransmission/transmission.h>
30 #include <libtransmission/log.h>
31 #include <libtransmission/rpcimpl.h>
32 #include <libtransmission/tr-getopt.h>
33 #include <libtransmission/utils.h>
34 #include <libtransmission/variant.h>
35 #include <libtransmission/version.h>
37 #define MY_NAME "transmission-remote"
38 #define DEFAULT_HOST "localhost"
39 #define DEFAULT_PORT atoi (TR_DEFAULT_RPC_PORT_STR)
40 #define DEFAULT_URL TR_DEFAULT_RPC_URL_STR "rpc/"
42 #define ARGUMENTS TR_KEY_arguments
46 #define MEM_K_STR "KiB"
47 #define MEM_M_STR "MiB"
48 #define MEM_G_STR "GiB"
49 #define MEM_T_STR "TiB"
52 #define DISK_B_STR "B"
53 #define DISK_K_STR "kB"
54 #define DISK_M_STR "MB"
55 #define DISK_G_STR "GB"
56 #define DISK_T_STR "TB"
59 #define SPEED_B_STR "B/s"
60 #define SPEED_K_STR "kB/s"
61 #define SPEED_M_STR "MB/s"
62 #define SPEED_G_STR "GB/s"
63 #define SPEED_T_STR "TB/s"
67 **** Display Utilities
72 etaToString (char * buf
, size_t buflen
, int64_t eta
)
75 tr_snprintf (buf
, buflen
, "Unknown");
77 tr_snprintf (buf
, buflen
, "%" PRId64
" sec", eta
);
78 else if (eta
< (60 * 60))
79 tr_snprintf (buf
, buflen
, "%" PRId64
" min", eta
/ 60);
80 else if (eta
< (60 * 60 * 24))
81 tr_snprintf (buf
, buflen
, "%" PRId64
" hrs", eta
/ (60 * 60));
83 tr_snprintf (buf
, buflen
, "%" PRId64
" days", eta
/ (60 * 60 * 24));
87 tr_strltime (char * buf
, int seconds
, size_t buflen
)
89 int days
, hours
, minutes
, total_seconds
;
90 char b
[128], d
[128], h
[128], m
[128], s
[128], t
[128];
95 total_seconds
= seconds
;
96 days
= seconds
/ 86400;
97 hours
= (seconds
% 86400) / 3600;
98 minutes
= (seconds
% 3600) / 60;
99 seconds
= (seconds
% 3600) % 60;
101 tr_snprintf (d
, sizeof (d
), "%d %s", days
, days
==1?"day":"days");
102 tr_snprintf (h
, sizeof (h
), "%d %s", hours
, hours
==1?"hour":"hours");
103 tr_snprintf (m
, sizeof (m
), "%d %s", minutes
, minutes
==1?"minute":"minutes");
104 tr_snprintf (s
, sizeof (s
), "%d %s", seconds
, seconds
==1?"second":"seconds");
105 tr_snprintf (t
, sizeof (t
), "%d %s", total_seconds
, total_seconds
==1?"second":"seconds");
109 if (days
>= 4 || !hours
)
110 tr_strlcpy (b
, d
, sizeof (b
));
112 tr_snprintf (b
, sizeof (b
), "%s, %s", d
, h
);
116 if (hours
>= 4 || !minutes
)
117 tr_strlcpy (b
, h
, sizeof (b
));
119 tr_snprintf (b
, sizeof (b
), "%s, %s", h
, m
);
123 if (minutes
>= 4 || !seconds
)
124 tr_strlcpy (b
, m
, sizeof (b
));
126 tr_snprintf (b
, sizeof (b
), "%s, %s", m
, s
);
128 else tr_strlcpy (b
, s
, sizeof (b
));
130 tr_snprintf (buf
, buflen
, "%s (%s)", b
, t
);
135 strlpercent (char * buf
, double x
, size_t buflen
)
137 return tr_strpercent (buf
, x
, buflen
);
141 strlratio2 (char * buf
, double ratio
, size_t buflen
)
143 return tr_strratio (buf
, buflen
, ratio
, "Inf");
147 strlratio (char * buf
, int64_t numerator
, int64_t denominator
, size_t buflen
)
151 if (denominator
!= 0)
152 ratio
= numerator
/ (double)denominator
;
153 else if (numerator
!= 0)
154 ratio
= TR_RATIO_INF
;
158 return strlratio2 (buf
, ratio
, buflen
);
162 strlmem (char * buf
, int64_t bytes
, size_t buflen
)
165 tr_strlcpy (buf
, "None", buflen
);
167 tr_formatter_mem_B (buf
, bytes
, buflen
);
173 strlsize (char * buf
, int64_t bytes
, size_t buflen
)
176 tr_strlcpy (buf
, "Unknown", buflen
);
178 tr_strlcpy (buf
, "None", buflen
);
180 tr_formatter_size_B (buf
, bytes
, buflen
);
203 MY_NAME
" "LONG_VERSION_STRING
"\n"
204 "A fast and easy BitTorrent client\n"
205 "http://www.transmissionbt.com/\n"
208 " [host] [options]\n"
210 MY_NAME
" [port] [options]\n"
212 MY_NAME
" [host:port] [options]\n"
214 MY_NAME
" [http(s?)://host:port/transmission/] [options]\n"
216 "See the man page for detailed explanations and many examples.";
221 **** Command-Line Arguments
225 static tr_option opts
[] =
227 { 'a', "add", "Add torrent files by filename or URL", "a", 0, NULL
},
228 { 970, "alt-speed", "Use the alternate Limits", "as", 0, NULL
},
229 { 971, "no-alt-speed", "Don't use the alternate Limits", "AS", 0, NULL
},
230 { 972, "alt-speed-downlimit", "max alternate download speed (in "SPEED_K_STR
")", "asd", 1, "<speed>" },
231 { 973, "alt-speed-uplimit", "max alternate upload speed (in "SPEED_K_STR
")", "asu", 1, "<speed>" },
232 { 974, "alt-speed-scheduler", "Use the scheduled on/off times", "asc", 0, NULL
},
233 { 975, "no-alt-speed-scheduler", "Don't use the scheduled on/off times", "ASC", 0, NULL
},
234 { 976, "alt-speed-time-begin", "Time to start using the alt speed limits (in hhmm)", NULL
, 1, "<time>" },
235 { 977, "alt-speed-time-end", "Time to stop using the alt speed limits (in hhmm)", NULL
, 1, "<time>" },
236 { 978, "alt-speed-days", "Numbers for any/all days of the week - eg. \"1-7\"", NULL
, 1, "<days>" },
237 { 963, "blocklist-update", "Blocklist update", NULL
, 0, NULL
},
238 { 'c', "incomplete-dir", "Where to store new torrents until they're complete", "c", 1, "<dir>" },
239 { 'C', "no-incomplete-dir", "Don't store incomplete torrents in a different location", "C", 0, NULL
},
240 { 'b', "debug", "Print debugging information", "b", 0, NULL
},
241 { 'd', "downlimit", "Set the max download speed in "SPEED_K_STR
" for the current torrent(s) or globally", "d", 1, "<speed>" },
242 { 'D', "no-downlimit", "Disable max download speed for the current torrent(s) or globally", "D", 0, NULL
},
243 { 'e', "cache", "Set the maximum size of the session's memory cache (in " MEM_M_STR
")", "e", 1, "<size>" },
244 { 910, "encryption-required", "Encrypt all peer connections", "er", 0, NULL
},
245 { 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", 0, NULL
},
246 { 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", 0, NULL
},
247 { 850, "exit", "Tell the transmission session to shut down", NULL
, 0, NULL
},
248 { 940, "files", "List the current torrent(s)' files", "f", 0, NULL
},
249 { 'g', "get", "Mark files for download", "g", 1, "<files>" },
250 { 'G', "no-get", "Mark files for not downloading", "G", 1, "<files>" },
251 { 'i', "info", "Show the current torrent(s)' details", "i", 0, NULL
},
252 { 940, "info-files", "List the current torrent(s)' files", "if", 0, NULL
},
253 { 941, "info-peers", "List the current torrent(s)' peers", "ip", 0, NULL
},
254 { 942, "info-pieces", "List the current torrent(s)' pieces", "ic", 0, NULL
},
255 { 943, "info-trackers", "List the current torrent(s)' trackers", "it", 0, NULL
},
256 { 920, "session-info", "Show the session's details", "si", 0, NULL
},
257 { 921, "session-stats", "Show the session's statistics", "st", 0, NULL
},
258 { 'l', "list", "List all torrents", "l", 0, NULL
},
259 { 960, "move", "Move current torrent's data to a new folder", NULL
, 1, "<path>" },
260 { 961, "find", "Tell Transmission where to find a torrent's data", NULL
, 1, "<path>" },
261 { 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", 0, NULL
},
262 { 'M', "no-portmap", "Disable portmapping", "M", 0, NULL
},
263 { 'n', "auth", "Set username and password", "n", 1, "<user:pw>" },
264 { 810, "authenv", "Set authentication info from the TR_AUTH environment variable (user:pw)", "ne", 0, NULL
},
265 { 'N', "netrc", "Set authentication info from a .netrc file", "N", 1, "<file>" },
266 { 820, "ssl", "Use SSL when talking to daemon", NULL
, 0, NULL
},
267 { 'o', "dht", "Enable distributed hash tables (DHT)", "o", 0, NULL
},
268 { 'O', "no-dht", "Disable distributed hash tables (DHT)", "O", 0, NULL
},
269 { 'p', "port", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR
")", "p", 1, "<port>" },
270 { 962, "port-test", "Port testing", "pt", 0, NULL
},
271 { 'P', "random-port", "Random port for incomping peers", "P", 0, NULL
},
272 { 900, "priority-high", "Try to download these file(s) first", "ph", 1, "<files>" },
273 { 901, "priority-normal", "Try to download these file(s) normally", "pn", 1, "<files>" },
274 { 902, "priority-low", "Try to download these file(s) last", "pl", 1, "<files>" },
275 { 700, "bandwidth-high", "Give this torrent first chance at available bandwidth", "Bh", 0, NULL
},
276 { 701, "bandwidth-normal", "Give this torrent bandwidth left over by high priority torrents", "Bn", 0, NULL
},
277 { 702, "bandwidth-low", "Give this torrent bandwidth left over by high and normal priority torrents", "Bl", 0, NULL
},
278 { 600, "reannounce", "Reannounce the current torrent(s)", NULL
, 0, NULL
},
279 { 'r', "remove", "Remove the current torrent(s)", "r", 0, NULL
},
280 { 930, "peers", "Set the maximum number of peers for the current torrent(s) or globally", "pr", 1, "<max>" },
281 { 'R', "remove-and-delete", "Remove the current torrent(s) and delete local data", NULL
, 0, NULL
},
282 { 800, "torrent-done-script", "Specify a script to run when a torrent finishes", NULL
, 1, "<file>" },
283 { 801, "no-torrent-done-script", "Don't run a script when torrents finish", NULL
, 0, NULL
},
284 { 950, "seedratio", "Let the current torrent(s) seed until a specific ratio", "sr", 1, "ratio" },
285 { 951, "seedratio-default", "Let the current torrent(s) use the global seedratio settings", "srd", 0, NULL
},
286 { 952, "no-seedratio", "Let the current torrent(s) seed regardless of ratio", "SR", 0, NULL
},
287 { 953, "global-seedratio", "All torrents, unless overridden by a per-torrent setting, should seed until a specific ratio", "gsr", 1, "ratio" },
288 { 954, "no-global-seedratio", "All torrents, unless overridden by a per-torrent setting, should seed regardless of ratio", "GSR", 0, NULL
},
289 { 710, "tracker-add", "Add a tracker to a torrent", "td", 1, "<tracker>" },
290 { 712, "tracker-remove", "Remove a tracker from a torrent", "tr", 1, "<trackerId>" },
291 { 's', "start", "Start the current torrent(s)", "s", 0, NULL
},
292 { 'S', "stop", "Stop the current torrent(s)", "S", 0, NULL
},
293 { 't', "torrent", "Set the current torrent(s)", "t", 1, "<torrent>" },
294 { 990, "start-paused", "Start added torrents paused", NULL
, 0, NULL
},
295 { 991, "no-start-paused", "Start added torrents unpaused", NULL
, 0, NULL
},
296 { 992, "trash-torrent", "Delete torrents after adding", NULL
, 0, NULL
},
297 { 993, "no-trash-torrent", "Do not delete torrents after adding", NULL
, 0, NULL
},
298 { 984, "honor-session", "Make the current torrent(s) honor the session limits", "hl", 0, NULL
},
299 { 985, "no-honor-session", "Make the current torrent(s) not honor the session limits", "HL", 0, NULL
},
300 { 'u', "uplimit", "Set the max upload speed in "SPEED_K_STR
" for the current torrent(s) or globally", "u", 1, "<speed>" },
301 { 'U', "no-uplimit", "Disable max upload speed for the current torrent(s) or globally", "U", 0, NULL
},
302 { 830, "utp", "Enable uTP for peer connections", NULL
, 0, NULL
},
303 { 831, "no-utp", "Disable uTP for peer connections", NULL
, 0, NULL
},
304 { 'v', "verify", "Verify the current torrent(s)", "v", 0, NULL
},
305 { 'V', "version", "Show version number and exit", "V", 0, NULL
},
306 { 'w', "download-dir", "When used in conjunction with --add, set the new torrent's download folder. Otherwise, set the default download folder", "w", 1, "<path>" },
307 { 'x', "pex", "Enable peer exchange (PEX)", "x", 0, NULL
},
308 { 'X', "no-pex", "Disable peer exchange (PEX)", "X", 0, NULL
},
309 { 'y', "lpd", "Enable local peer discovery (LPD)", "y", 0, NULL
},
310 { 'Y', "no-lpd", "Disable local peer discovery (LPD)", "Y", 0, NULL
},
311 { 941, "peer-info", "List the current torrent(s)' peers", "pi", 0, NULL
},
312 { 0, NULL
, NULL
, NULL
, 0, NULL
}
318 tr_getopt_usage (MY_NAME
, getUsage (), opts
);
322 numarg (const char * arg
)
325 const long num
= strtol (arg
, &end
, 10);
329 fprintf (stderr
, "Not a number: \"%s\"\n", arg
);
339 MODE_TORRENT_START
= (1<<0),
340 MODE_TORRENT_STOP
= (1<<1),
341 MODE_TORRENT_VERIFY
= (1<<2),
342 MODE_TORRENT_REANNOUNCE
= (1<<3),
343 MODE_TORRENT_SET
= (1<<4),
344 MODE_TORRENT_GET
= (1<<5),
345 MODE_TORRENT_ADD
= (1<<6),
346 MODE_TORRENT_REMOVE
= (1<<7),
347 MODE_TORRENT_SET_LOCATION
= (1<<8),
348 MODE_SESSION_SET
= (1<<9),
349 MODE_SESSION_GET
= (1<<10),
350 MODE_SESSION_STATS
= (1<<11),
351 MODE_SESSION_CLOSE
= (1<<12),
352 MODE_BLOCKLIST_UPDATE
= (1<<13),
353 MODE_PORT_TEST
= (1<<14)
363 case 'a': /* add torrent */
364 case 'b': /* debug */
366 case 810: /* authenv */
367 case 'N': /* netrc */
368 case 820: /* UseSSL */
369 case 't': /* set current torrent */
370 case 'V': /* show version number */
373 case 'c': /* incomplete-dir */
374 case 'C': /* no-incomplete-dir */
375 case 'e': /* cache */
376 case 'm': /* portmap */
377 case 'M': /* "no-portmap */
379 case 'O': /* no-dht */
380 case 'p': /* incoming peer port */
381 case 'P': /* random incoming peer port */
383 case 'X': /* no-pex */
385 case 'Y': /* no-lpd */
386 case 800: /* torrent-done-script */
387 case 801: /* no-torrent-done-script */
389 case 831: /* no-utp */
390 case 970: /* alt-speed */
391 case 971: /* no-alt-speed */
392 case 972: /* alt-speed-downlimit */
393 case 973: /* alt-speed-uplimit */
394 case 974: /* alt-speed-scheduler */
395 case 975: /* no-alt-speed-scheduler */
396 case 976: /* alt-speed-time-begin */
397 case 977: /* alt-speed-time-end */
398 case 978: /* alt-speed-days */
399 case 910: /* encryption-required */
400 case 911: /* encryption-preferred */
401 case 912: /* encryption-tolerated */
402 case 953: /* global-seedratio */
403 case 954: /* no-global-seedratio */
404 case 990: /* start-paused */
405 case 991: /* no-start-paused */
406 case 992: /* trash-torrent */
407 case 993: /* no-trash-torrent */
408 return MODE_SESSION_SET
;
410 case 712: /* tracker-remove */
411 case 950: /* seedratio */
412 case 951: /* seedratio-default */
413 case 952: /* no-seedratio */
414 case 984: /* honor-session */
415 case 985: /* no-honor-session */
416 return MODE_TORRENT_SET
;
418 case 920: /* session-info */
419 return MODE_SESSION_GET
;
422 case 'G': /* no-get */
423 case 700: /* torrent priority-high */
424 case 701: /* torrent priority-normal */
425 case 702: /* torrent priority-low */
426 case 710: /* tracker-add */
427 case 900: /* file priority-high */
428 case 901: /* file priority-normal */
429 case 902: /* file priority-low */
430 return MODE_TORRENT_SET
| MODE_TORRENT_ADD
;
433 return MODE_TORRENT_SET_LOCATION
| MODE_TORRENT_ADD
;
436 case 'l': /* list all torrents */
437 case 940: /* info-files */
438 case 941: /* info-peer */
439 case 942: /* info-pieces */
440 case 943: /* info-tracker */
441 return MODE_TORRENT_GET
;
443 case 'd': /* download speed limit */
444 case 'D': /* no download speed limit */
445 case 'u': /* upload speed limit */
446 case 'U': /* no upload speed limit */
447 case 930: /* peers */
448 return MODE_SESSION_SET
| MODE_TORRENT_SET
;
450 case 's': /* start */
451 return MODE_TORRENT_START
| MODE_TORRENT_ADD
;
454 return MODE_TORRENT_STOP
| MODE_TORRENT_ADD
;
456 case 'w': /* download-dir */
457 return MODE_SESSION_SET
| MODE_TORRENT_ADD
;
459 case 850: /* session-close */
460 return MODE_SESSION_CLOSE
;
462 case 963: /* blocklist-update */
463 return MODE_BLOCKLIST_UPDATE
;
465 case 921: /* session-stats */
466 return MODE_SESSION_STATS
;
468 case 'v': /* verify */
469 return MODE_TORRENT_VERIFY
;
471 case 600: /* reannounce */
472 return MODE_TORRENT_REANNOUNCE
;
474 case 962: /* port-test */
475 return MODE_PORT_TEST
;
477 case 'r': /* remove */
478 case 'R': /* remove and delete */
479 return MODE_TORRENT_REMOVE
;
482 return MODE_TORRENT_SET_LOCATION
;
485 fprintf (stderr
, "unrecognized argument %d\n", val
);
486 assert ("unrecognized argument" && 0);
491 static bool debug
= 0;
492 static char * auth
= NULL
;
493 static char * netrc
= NULL
;
494 static char * sessionId
= NULL
;
495 static bool UseSSL
= false;
504 result
= _getcwd (buf
, sizeof (buf
));
506 result
= getcwd (buf
, sizeof (buf
));
511 fprintf (stderr
, "getcwd error: \"%s\"", tr_strerror (errno
));
515 return tr_strdup (buf
);
519 absolutify (const char * path
)
525 buf
= tr_strdup (path
);
529 char * cwd
= tr_getcwd ();
530 buf
= tr_buildPath (cwd
, path
, NULL
);
538 getEncodedMetainfo (const char * filename
)
542 uint8_t * buf
= tr_loadFile (filename
, &len
);
546 b64
= tr_base64_encode (buf
, len
, NULL
);
553 addIdArg (tr_variant
* args
, const char * id
, const char * fallback
)
561 fprintf (stderr
, "No torrent specified! Please use the -t option first.\n");
562 id
= "-1"; /* no torrent will have this ID, so will act as a no-op */
566 if (!tr_strcmp0 (id
, "active"))
568 tr_variantDictAddStr (args
, TR_KEY_ids
, "recently-active");
570 else if (strcmp (id
, "all"))
573 bool isList
= strchr (id
,',') || strchr (id
,'-');
576 for (pch
=id
; isNum
&& *pch
; ++pch
)
581 tr_rpc_parse_list_str (tr_variantDictAdd (args
, TR_KEY_ids
), id
, strlen (id
));
583 tr_variantDictAddStr (args
, TR_KEY_ids
, id
); /* it's a torrent sha hash */
588 addTime (tr_variant
* args
, const tr_quark key
, const char * arg
)
591 bool success
= false;
593 if (arg
&& (strlen (arg
) == 4))
595 const char hh
[3] = { arg
[0], arg
[1], '\0' };
596 const char mm
[3] = { arg
[2], arg
[3], '\0' };
597 const int hour
= atoi (hh
);
598 const int min
= atoi (mm
);
600 if (0<=hour
&& hour
<24 && 0<=min
&& min
<60)
602 time
= min
+ (hour
* 60);
608 tr_variantDictAddInt (args
, key
, time
);
610 fprintf (stderr
, "Please specify the time of day in 'hhmm' format.\n");
614 addDays (tr_variant
* args
, const tr_quark key
, const char * arg
)
624 values
= tr_parseNumberRange (arg
, -1, &valueCount
);
625 for (i
=0; i
<valueCount
; ++i
)
627 if (values
[i
] < 0 || values
[i
] > 7)
633 days
|= 1 << values
[i
];
640 tr_variantDictAddInt (args
, key
, days
);
642 fprintf (stderr
, "Please specify the days of the week in '1-3,4,7' format.\n");
646 addFiles (tr_variant
* args
,
650 tr_variant
* files
= tr_variantDictAddList (args
, key
, 100);
654 fprintf (stderr
, "No files specified!\n");
655 arg
= "-1"; /* no file will have this index, so should be a no-op */
658 if (strcmp (arg
, "all"))
662 int * values
= tr_parseNumberRange (arg
, -1, &valueCount
);
664 for (i
=0; i
<valueCount
; ++i
)
665 tr_variantListAddInt (files
, values
[i
]);
671 #define TR_N_ELEMENTS(ary) (sizeof (ary) / sizeof (*ary))
673 static const tr_quark files_keys
[] = {
680 static const tr_quark details_keys
[] = {
683 TR_KEY_bandwidthPriority
,
688 TR_KEY_desiredAvailable
,
691 TR_KEY_downloadedEver
,
692 TR_KEY_downloadLimit
,
693 TR_KEY_downloadLimited
,
698 TR_KEY_haveUnchecked
,
700 TR_KEY_honorsSessionLimits
,
704 TR_KEY_leftUntilDone
,
707 TR_KEY_peersConnected
,
708 TR_KEY_peersGettingFromUs
,
709 TR_KEY_peersSendingToUs
,
715 TR_KEY_recheckProgress
,
716 TR_KEY_secondsDownloading
,
717 TR_KEY_secondsSeeding
,
718 TR_KEY_seedRatioMode
,
719 TR_KEY_seedRatioLimit
,
726 TR_KEY_uploadLimited
,
728 TR_KEY_webseedsSendingToUs
731 static const tr_quark list_keys
[] = {
737 TR_KEY_leftUntilDone
,
739 TR_KEY_peersGettingFromUs
,
740 TR_KEY_peersSendingToUs
,
749 writeFunc (void * ptr
, size_t size
, size_t nmemb
, void * buf
)
751 const size_t byteCount
= size
* nmemb
;
752 evbuffer_add (buf
, ptr
, byteCount
);
756 /* look for a session id in the header in case the server gives back a 409 */
758 parseResponseHeader (void *ptr
, size_t size
, size_t nmemb
, void * stream UNUSED
)
760 const char * line
= ptr
;
761 const size_t line_len
= size
* nmemb
;
762 const char * key
= TR_RPC_SESSION_ID_HEADER
": ";
763 const size_t key_len
= strlen (key
);
765 if ((line_len
>= key_len
) && !memcmp (line
, key
, key_len
))
767 const char * begin
= line
+ key_len
;
768 const char * end
= begin
;
769 while (!isspace (*end
))
772 sessionId
= tr_strndup (begin
, end
-begin
);
779 getTimeoutSecs (const char * req
)
781 if (strstr (req
, "\"method\":\"blocklist-update\"") != NULL
)
784 return 60L; /* default value */
788 getStatusString (tr_variant
* t
, char * buf
, size_t buflen
)
793 if (!tr_variantDictFindInt (t
, TR_KEY_status
, &status
))
799 case TR_STATUS_DOWNLOAD_WAIT
:
800 case TR_STATUS_SEED_WAIT
:
801 tr_strlcpy (buf
, "Queued", buflen
);
804 case TR_STATUS_STOPPED
:
805 if (tr_variantDictFindBool (t
, TR_KEY_isFinished
, &boolVal
) && boolVal
)
806 tr_strlcpy (buf
, "Finished", buflen
);
808 tr_strlcpy (buf
, "Stopped", buflen
);
811 case TR_STATUS_CHECK_WAIT
:
812 case TR_STATUS_CHECK
: {
813 const char * str
= status
== TR_STATUS_CHECK_WAIT
817 if (tr_variantDictFindReal (t
, TR_KEY_recheckProgress
, &percent
))
818 tr_snprintf (buf
, buflen
, "%s (%.0f%%)", str
, floor (percent
*100.0));
820 tr_strlcpy (buf
, str
, buflen
);
825 case TR_STATUS_DOWNLOAD
:
826 case TR_STATUS_SEED
: {
829 tr_variantDictFindInt (t
, TR_KEY_peersGettingFromUs
, &fromUs
);
830 tr_variantDictFindInt (t
, TR_KEY_peersSendingToUs
, &toUs
);
832 tr_strlcpy (buf
, "Up & Down", buflen
);
834 tr_strlcpy (buf
, "Downloading", buflen
);
836 int64_t leftUntilDone
= 0;
837 tr_variantDictFindInt (t
, TR_KEY_leftUntilDone
, &leftUntilDone
);
838 if (leftUntilDone
> 0)
839 tr_strlcpy (buf
, "Uploading", buflen
);
841 tr_strlcpy (buf
, "Seeding", buflen
);
843 tr_strlcpy (buf
, "Idle", buflen
);
849 tr_strlcpy (buf
, "Unknown", buflen
);
856 static const char *bandwidthPriorityNames
[] =
857 { "Low", "Normal", "High", "Invalid" };
860 printDetails (tr_variant
* top
)
862 tr_variant
*args
, *torrents
;
864 if ((tr_variantDictFindDict (top
, TR_KEY_arguments
, &args
))
865 && (tr_variantDictFindList (args
, TR_KEY_torrents
, &torrents
)))
868 for (ti
= 0, tCount
= tr_variantListSize (torrents
); ti
< tCount
;
871 tr_variant
* t
= tr_variantListChild (torrents
, ti
);
881 if (tr_variantDictFindInt (t
, TR_KEY_id
, &i
))
882 printf (" Id: %" PRId64
"\n", i
);
883 if (tr_variantDictFindStr (t
, TR_KEY_name
, &str
, NULL
))
884 printf (" Name: %s\n", str
);
885 if (tr_variantDictFindStr (t
, TR_KEY_hashString
, &str
, NULL
))
886 printf (" Hash: %s\n", str
);
887 if (tr_variantDictFindStr (t
, TR_KEY_magnetLink
, &str
, NULL
))
888 printf (" Magnet: %s\n", str
);
891 printf ("TRANSFER\n");
892 getStatusString (t
, buf
, sizeof (buf
));
893 printf (" State: %s\n", buf
);
895 if (tr_variantDictFindStr (t
, TR_KEY_downloadDir
, &str
, NULL
))
896 printf (" Location: %s\n", str
);
898 if (tr_variantDictFindInt (t
, TR_KEY_sizeWhenDone
, &i
)
899 && tr_variantDictFindInt (t
, TR_KEY_leftUntilDone
, &j
))
901 strlpercent (buf
, 100.0 * (i
- j
) / i
, sizeof (buf
));
902 printf (" Percent Done: %s%%\n", buf
);
905 if (tr_variantDictFindInt (t
, TR_KEY_eta
, &i
))
906 printf (" ETA: %s\n", tr_strltime (buf
, i
, sizeof (buf
)));
907 if (tr_variantDictFindInt (t
, TR_KEY_rateDownload
, &i
))
908 printf (" Download Speed: %s\n", tr_formatter_speed_KBps (buf
, i
/ (double)tr_speed_K
, sizeof (buf
)));
909 if (tr_variantDictFindInt (t
, TR_KEY_rateUpload
, &i
))
910 printf (" Upload Speed: %s\n", tr_formatter_speed_KBps (buf
, i
/ (double)tr_speed_K
, sizeof (buf
)));
911 if (tr_variantDictFindInt (t
, TR_KEY_haveUnchecked
, &i
)
912 && tr_variantDictFindInt (t
, TR_KEY_haveValid
, &j
))
914 strlsize (buf
, i
+ j
, sizeof (buf
));
915 strlsize (buf2
, j
, sizeof (buf2
));
916 printf (" Have: %s (%s verified)\n", buf
, buf2
);
919 if (tr_variantDictFindInt (t
, TR_KEY_sizeWhenDone
, &i
))
922 printf (" Availability: None\n");
923 if (tr_variantDictFindInt (t
, TR_KEY_desiredAvailable
, &j
)
924 && tr_variantDictFindInt (t
, TR_KEY_leftUntilDone
, &k
))
927 strlpercent (buf
, 100.0 * j
/ i
, sizeof (buf
));
928 printf (" Availability: %s%%\n", buf
);
930 if (tr_variantDictFindInt (t
, TR_KEY_totalSize
, &j
))
932 strlsize (buf2
, i
, sizeof (buf2
));
933 strlsize (buf
, j
, sizeof (buf
));
934 printf (" Total size: %s (%s wanted)\n", buf
, buf2
);
937 if (tr_variantDictFindInt (t
, TR_KEY_downloadedEver
, &i
)
938 && tr_variantDictFindInt (t
, TR_KEY_uploadedEver
, &j
))
940 strlsize (buf
, i
, sizeof (buf
));
941 printf (" Downloaded: %s\n", buf
);
942 strlsize (buf
, j
, sizeof (buf
));
943 printf (" Uploaded: %s\n", buf
);
944 strlratio (buf
, j
, i
, sizeof (buf
));
945 printf (" Ratio: %s\n", buf
);
947 if (tr_variantDictFindInt (t
, TR_KEY_corruptEver
, &i
))
949 strlsize (buf
, i
, sizeof (buf
));
950 printf (" Corrupt DL: %s\n", buf
);
952 if (tr_variantDictFindStr (t
, TR_KEY_errorString
, &str
, NULL
) && str
&& *str
&&
953 tr_variantDictFindInt (t
, TR_KEY_error
, &i
) && i
)
956 case TR_STAT_TRACKER_WARNING
: printf (" Tracker gave a warning: %s\n", str
); break;
957 case TR_STAT_TRACKER_ERROR
: printf (" Tracker gave an error: %s\n", str
); break;
958 case TR_STAT_LOCAL_ERROR
: printf (" Error: %s\n", str
); break;
959 default: break; /* no error */
962 if (tr_variantDictFindInt (t
, TR_KEY_peersConnected
, &i
)
963 && tr_variantDictFindInt (t
, TR_KEY_peersGettingFromUs
, &j
)
964 && tr_variantDictFindInt (t
, TR_KEY_peersSendingToUs
, &k
))
968 "connected to %" PRId64
", "
969 "uploading to %" PRId64
976 if (tr_variantDictFindList (t
, TR_KEY_webseeds
, &l
)
977 && tr_variantDictFindInt (t
, TR_KEY_webseedsSendingToUs
, &i
))
979 const int64_t n
= tr_variantListSize (l
);
982 " Web Seeds: downloading from %" PRId64
" of %"
984 " web seeds\n", i
, n
);
988 printf ("HISTORY\n");
989 if (tr_variantDictFindInt (t
, TR_KEY_addedDate
, &i
) && i
)
992 printf (" Date added: %s", ctime (&tt
));
994 if (tr_variantDictFindInt (t
, TR_KEY_doneDate
, &i
) && i
)
997 printf (" Date finished: %s", ctime (&tt
));
999 if (tr_variantDictFindInt (t
, TR_KEY_startDate
, &i
) && i
)
1001 const time_t tt
= i
;
1002 printf (" Date started: %s", ctime (&tt
));
1004 if (tr_variantDictFindInt (t
, TR_KEY_activityDate
, &i
) && i
)
1006 const time_t tt
= i
;
1007 printf (" Latest activity: %s", ctime (&tt
));
1009 if (tr_variantDictFindInt (t
, TR_KEY_secondsDownloading
, &i
) && (i
> 0))
1010 printf (" Downloading Time: %s\n", tr_strltime (buf
, i
, sizeof (buf
)));
1011 if (tr_variantDictFindInt (t
, TR_KEY_secondsSeeding
, &i
) && (i
> 0))
1012 printf (" Seeding Time: %s\n", tr_strltime (buf
, i
, sizeof (buf
)));
1015 printf ("ORIGINS\n");
1016 if (tr_variantDictFindInt (t
, TR_KEY_dateCreated
, &i
) && i
)
1018 const time_t tt
= i
;
1019 printf (" Date created: %s", ctime (&tt
));
1021 if (tr_variantDictFindBool (t
, TR_KEY_isPrivate
, &boolVal
))
1022 printf (" Public torrent: %s\n", (boolVal
? "No" : "Yes"));
1023 if (tr_variantDictFindStr (t
, TR_KEY_comment
, &str
, NULL
) && str
&& *str
)
1024 printf (" Comment: %s\n", str
);
1025 if (tr_variantDictFindStr (t
, TR_KEY_creator
, &str
, NULL
) && str
&& *str
)
1026 printf (" Creator: %s\n", str
);
1027 if (tr_variantDictFindInt (t
, TR_KEY_pieceCount
, &i
))
1028 printf (" Piece Count: %" PRId64
"\n", i
);
1029 if (tr_variantDictFindInt (t
, TR_KEY_pieceSize
, &i
))
1030 printf (" Piece Size: %s\n", strlmem (buf
, i
, sizeof (buf
)));
1033 printf ("LIMITS & BANDWIDTH\n");
1034 if (tr_variantDictFindBool (t
, TR_KEY_downloadLimited
, &boolVal
)
1035 && tr_variantDictFindInt (t
, TR_KEY_downloadLimit
, &i
))
1037 printf (" Download Limit: ");
1039 printf ("%s\n", tr_formatter_speed_KBps (buf
, i
, sizeof (buf
)));
1041 printf ("Unlimited\n");
1043 if (tr_variantDictFindBool (t
, TR_KEY_uploadLimited
, &boolVal
)
1044 && tr_variantDictFindInt (t
, TR_KEY_uploadLimit
, &i
))
1046 printf (" Upload Limit: ");
1048 printf ("%s\n", tr_formatter_speed_KBps (buf
, i
, sizeof (buf
)));
1050 printf ("Unlimited\n");
1052 if (tr_variantDictFindInt (t
, TR_KEY_seedRatioMode
, &i
))
1055 case TR_RATIOLIMIT_GLOBAL
:
1056 printf (" Ratio Limit: Default\n");
1058 case TR_RATIOLIMIT_SINGLE
:
1059 if (tr_variantDictFindReal (t
, TR_KEY_seedRatioLimit
, &d
))
1060 printf (" Ratio Limit: %s\n", strlratio2 (buf
, d
, sizeof(buf
)));
1062 case TR_RATIOLIMIT_UNLIMITED
:
1063 printf (" Ratio Limit: Unlimited\n");
1068 if (tr_variantDictFindBool (t
, TR_KEY_honorsSessionLimits
, &boolVal
))
1069 printf (" Honors Session Limits: %s\n", (boolVal
? "Yes" : "No"));
1070 if (tr_variantDictFindInt (t
, TR_KEY_peer_limit
, &i
))
1071 printf (" Peer limit: %" PRId64
"\n", i
);
1072 if (tr_variantDictFindInt (t
, TR_KEY_bandwidthPriority
, &i
))
1073 printf (" Bandwidth Priority: %s\n",
1074 bandwidthPriorityNames
[ (i
+ 1) & 3]);
1082 printFileList (tr_variant
* top
)
1084 tr_variant
*args
, *torrents
;
1086 if ((tr_variantDictFindDict (top
, TR_KEY_arguments
, &args
))
1087 && (tr_variantDictFindList (args
, TR_KEY_torrents
, &torrents
)))
1090 for (i
= 0, in
= tr_variantListSize (torrents
); i
< in
; ++i
)
1092 tr_variant
* d
= tr_variantListChild (torrents
, i
);
1093 tr_variant
* files
, *priorities
, *wanteds
;
1095 if (tr_variantDictFindStr (d
, TR_KEY_name
, &name
, NULL
)
1096 && tr_variantDictFindList (d
, TR_KEY_files
, &files
)
1097 && tr_variantDictFindList (d
, TR_KEY_priorities
, &priorities
)
1098 && tr_variantDictFindList (d
, TR_KEY_wanted
, &wanteds
))
1100 int j
= 0, jn
= tr_variantListSize (files
);
1101 printf ("%s (%d files):\n", name
, jn
);
1102 printf ("%3s %4s %8s %3s %9s %s\n", "#", "Done",
1103 "Priority", "Get", "Size",
1105 for (j
= 0, jn
= tr_variantListSize (files
); j
< jn
; ++j
)
1111 const char * filename
;
1112 tr_variant
* file
= tr_variantListChild (files
, j
);
1113 if (tr_variantDictFindInt (file
, TR_KEY_length
, &length
)
1114 && tr_variantDictFindStr (file
, TR_KEY_name
, &filename
, NULL
)
1115 && tr_variantDictFindInt (file
, TR_KEY_bytesCompleted
, &have
)
1116 && tr_variantGetInt (tr_variantListChild (priorities
, j
), &priority
)
1117 && tr_variantGetInt (tr_variantListChild (wanteds
, j
), &wanted
))
1120 double percent
= (double)have
/ length
;
1121 const char * pristr
;
1122 strlsize (sizestr
, length
, sizeof (sizestr
));
1126 pristr
= "Low"; break;
1129 pristr
= "High"; break;
1132 pristr
= "Normal"; break;
1134 printf ("%3d: %3.0f%% %-8s %-3s %9s %s\n",
1136 floor (100.0 * percent
),
1138 (wanted
? "Yes" : "No"),
1149 printPeersImpl (tr_variant
* peers
)
1152 printf ("%-20s %-12s %-5s %-6s %-6s %s\n",
1153 "Address", "Flags", "Done", "Down", "Up", "Client");
1155 for (i
=0, n
=tr_variantListSize(peers
); i
<n
; ++i
)
1158 const char * address
, * client
, * flagstr
;
1159 int64_t rateToClient
, rateToPeer
;
1160 tr_variant
* d
= tr_variantListChild (peers
, i
);
1162 if (tr_variantDictFindStr (d
, TR_KEY_address
, &address
, NULL
)
1163 && tr_variantDictFindStr (d
, TR_KEY_clientName
, &client
, NULL
)
1164 && tr_variantDictFindReal (d
, TR_KEY_progress
, &progress
)
1165 && tr_variantDictFindStr (d
, TR_KEY_flagStr
, &flagstr
, NULL
)
1166 && tr_variantDictFindInt (d
, TR_KEY_rateToClient
, &rateToClient
)
1167 && tr_variantDictFindInt (d
, TR_KEY_rateToPeer
, &rateToPeer
))
1169 printf ("%-20s %-12s %-5.1f %6.1f %6.1f %s\n",
1170 address
, flagstr
, (progress
*100.0),
1171 rateToClient
/ (double)tr_speed_K
,
1172 rateToPeer
/ (double)tr_speed_K
,
1179 printPeers (tr_variant
* top
)
1181 tr_variant
*args
, *torrents
;
1183 if (tr_variantDictFindDict (top
, TR_KEY_arguments
, &args
)
1184 && tr_variantDictFindList (args
, TR_KEY_torrents
, &torrents
))
1187 for (i
=0, n
=tr_variantListSize (torrents
); i
<n
; ++i
)
1190 tr_variant
* torrent
= tr_variantListChild (torrents
, i
);
1191 if (tr_variantDictFindList (torrent
, TR_KEY_peers
, &peers
))
1193 printPeersImpl (peers
);
1202 printPiecesImpl (const uint8_t * raw
, size_t rawlen
, int64_t j
)
1205 char * str
= tr_base64_decode (raw
, rawlen
, &len
);
1207 for (i
=k
=0; k
<len
; ++k
) {
1209 for (e
=0; i
<j
&& e
<8; ++e
, ++i
)
1210 printf ("%c", str
[k
] & (1<< (7-e
)) ? '1' : '0');
1220 printPieces (tr_variant
* top
)
1222 tr_variant
*args
, *torrents
;
1224 if (tr_variantDictFindDict (top
, TR_KEY_arguments
, &args
)
1225 && tr_variantDictFindList (args
, TR_KEY_torrents
, &torrents
))
1228 for (i
=0, n
=tr_variantListSize (torrents
); i
<n
; ++i
)
1231 const uint8_t * raw
;
1233 tr_variant
* torrent
= tr_variantListChild (torrents
, i
);
1234 if (tr_variantDictFindRaw (torrent
, TR_KEY_pieces
, &raw
, &rawlen
) &&
1235 tr_variantDictFindInt (torrent
, TR_KEY_pieceCount
, &j
)) {
1236 printPiecesImpl (raw
, rawlen
, j
);
1245 printPortTest (tr_variant
* top
)
1248 if ((tr_variantDictFindDict (top
, TR_KEY_arguments
, &args
)))
1252 if (tr_variantDictFindBool (args
, TR_KEY_port_is_open
, &boolVal
))
1253 printf ("Port is open: %s\n", (boolVal
? "Yes" : "No"));
1258 printTorrentList (tr_variant
* top
)
1260 tr_variant
*args
, *list
;
1262 if ((tr_variantDictFindDict (top
, TR_KEY_arguments
, &args
))
1263 && (tr_variantDictFindList (args
, TR_KEY_torrents
, &list
)))
1266 int64_t total_size
=0;
1267 double total_up
=0, total_down
=0;
1270 printf ("%-4s %-4s %9s %-8s %6s %6s %-5s %-11s %s\n",
1271 "ID", "Done", "Have", "ETA", "Up", "Down", "Ratio", "Status",
1274 for (i
= 0, n
= tr_variantListSize (list
); i
< n
; ++i
)
1276 int64_t id
, eta
, status
, up
, down
;
1277 int64_t sizeWhenDone
, leftUntilDone
;
1280 tr_variant
* d
= tr_variantListChild (list
, i
);
1281 if (tr_variantDictFindInt (d
, TR_KEY_eta
, &eta
)
1282 && tr_variantDictFindInt (d
, TR_KEY_id
, &id
)
1283 && tr_variantDictFindInt (d
, TR_KEY_leftUntilDone
, &leftUntilDone
)
1284 && tr_variantDictFindStr (d
, TR_KEY_name
, &name
, NULL
)
1285 && tr_variantDictFindInt (d
, TR_KEY_rateDownload
, &down
)
1286 && tr_variantDictFindInt (d
, TR_KEY_rateUpload
, &up
)
1287 && tr_variantDictFindInt (d
, TR_KEY_sizeWhenDone
, &sizeWhenDone
)
1288 && tr_variantDictFindInt (d
, TR_KEY_status
, &status
)
1289 && tr_variantDictFindReal (d
, TR_KEY_uploadRatio
, &ratio
))
1299 tr_snprintf (doneStr
, sizeof (doneStr
), "%d%%", (int)(100.0 * (sizeWhenDone
- leftUntilDone
) / sizeWhenDone
));
1301 tr_strlcpy (doneStr
, "n/a", sizeof (doneStr
));
1303 strlsize (haveStr
, sizeWhenDone
- leftUntilDone
, sizeof (haveStr
));
1305 if (leftUntilDone
|| eta
!= -1)
1306 etaToString (etaStr
, sizeof (etaStr
), eta
);
1308 tr_snprintf (etaStr
, sizeof (etaStr
), "Done");
1309 if (tr_variantDictFindInt (d
, TR_KEY_error
, &error
) && error
)
1314 "%4d%c %4s %9s %-8s %6.1f %6.1f %5s %-11s %s\n",
1319 up
/ (double)tr_speed_K
,
1320 down
/ (double)tr_speed_K
,
1321 strlratio2 (ratioStr
, ratio
, sizeof (ratioStr
)),
1322 getStatusString (d
, statusStr
, sizeof (statusStr
)),
1327 total_size
+= sizeWhenDone
- leftUntilDone
;
1331 printf ("Sum: %9s %6.1f %6.1f\n",
1332 strlsize (haveStr
, total_size
, sizeof (haveStr
)),
1333 total_up
/ (double)tr_speed_K
,
1334 total_down
/ (double)tr_speed_K
);
1339 printTrackersImpl (tr_variant
* trackerStats
)
1345 for (i
=0; ((t
= tr_variantListChild (trackerStats
, i
))); ++i
)
1347 int64_t downloadCount
;
1353 int64_t lastAnnouncePeerCount
;
1354 const char * lastAnnounceResult
;
1355 int64_t lastAnnounceStartTime
;
1356 bool lastAnnounceSucceeded
;
1357 int64_t lastAnnounceTime
;
1358 bool lastAnnounceTimedOut
;
1359 const char * lastScrapeResult
;
1360 bool lastScrapeSucceeded
;
1361 int64_t lastScrapeStartTime
;
1362 int64_t lastScrapeTime
;
1363 bool lastScrapeTimedOut
;
1364 int64_t leecherCount
;
1365 int64_t nextAnnounceTime
;
1366 int64_t nextScrapeTime
;
1367 int64_t seederCount
;
1369 int64_t announceState
;
1370 int64_t scrapeState
;
1372 if (tr_variantDictFindInt (t
, TR_KEY_downloadCount
, &downloadCount
) &&
1373 tr_variantDictFindBool (t
, TR_KEY_hasAnnounced
, &hasAnnounced
) &&
1374 tr_variantDictFindBool (t
, TR_KEY_hasScraped
, &hasScraped
) &&
1375 tr_variantDictFindStr (t
, TR_KEY_host
, &host
, NULL
) &&
1376 tr_variantDictFindInt (t
, TR_KEY_id
, &id
) &&
1377 tr_variantDictFindBool (t
, TR_KEY_isBackup
, &isBackup
) &&
1378 tr_variantDictFindInt (t
, TR_KEY_announceState
, &announceState
) &&
1379 tr_variantDictFindInt (t
, TR_KEY_scrapeState
, &scrapeState
) &&
1380 tr_variantDictFindInt (t
, TR_KEY_lastAnnouncePeerCount
, &lastAnnouncePeerCount
) &&
1381 tr_variantDictFindStr (t
, TR_KEY_lastAnnounceResult
, &lastAnnounceResult
, NULL
) &&
1382 tr_variantDictFindInt (t
, TR_KEY_lastAnnounceStartTime
, &lastAnnounceStartTime
) &&
1383 tr_variantDictFindBool (t
, TR_KEY_lastAnnounceSucceeded
, &lastAnnounceSucceeded
) &&
1384 tr_variantDictFindInt (t
, TR_KEY_lastAnnounceTime
, &lastAnnounceTime
) &&
1385 tr_variantDictFindBool (t
, TR_KEY_lastAnnounceTimedOut
, &lastAnnounceTimedOut
) &&
1386 tr_variantDictFindStr (t
, TR_KEY_lastScrapeResult
, &lastScrapeResult
, NULL
) &&
1387 tr_variantDictFindInt (t
, TR_KEY_lastScrapeStartTime
, &lastScrapeStartTime
) &&
1388 tr_variantDictFindBool (t
, TR_KEY_lastScrapeSucceeded
, &lastScrapeSucceeded
) &&
1389 tr_variantDictFindInt (t
, TR_KEY_lastScrapeTime
, &lastScrapeTime
) &&
1390 tr_variantDictFindBool (t
, TR_KEY_lastScrapeTimedOut
, &lastScrapeTimedOut
) &&
1391 tr_variantDictFindInt (t
, TR_KEY_leecherCount
, &leecherCount
) &&
1392 tr_variantDictFindInt (t
, TR_KEY_nextAnnounceTime
, &nextAnnounceTime
) &&
1393 tr_variantDictFindInt (t
, TR_KEY_nextScrapeTime
, &nextScrapeTime
) &&
1394 tr_variantDictFindInt (t
, TR_KEY_seederCount
, &seederCount
) &&
1395 tr_variantDictFindInt (t
, TR_KEY_tier
, &tier
))
1397 const time_t now
= time (NULL
);
1400 printf (" Tracker %d: %s\n", (int)(id
), host
);
1402 printf (" Backup on tier %d\n", (int)tier
);
1404 printf (" Active in tier %d\n", (int)tier
);
1408 if (hasAnnounced
&& announceState
!= TR_TRACKER_INACTIVE
)
1410 tr_strltime (buf
, now
- lastAnnounceTime
, sizeof (buf
));
1411 if (lastAnnounceSucceeded
)
1412 printf (" Got a list of %d peers %s ago\n",
1413 (int)lastAnnouncePeerCount
, buf
);
1414 else if (lastAnnounceTimedOut
)
1415 printf (" Peer list request timed out; will retry\n");
1417 printf (" Got an error \"%s\" %s ago\n",
1418 lastAnnounceResult
, buf
);
1421 switch (announceState
)
1423 case TR_TRACKER_INACTIVE
:
1424 printf (" No updates scheduled\n");
1426 case TR_TRACKER_WAITING
:
1427 tr_strltime (buf
, nextAnnounceTime
- now
, sizeof (buf
));
1428 printf (" Asking for more peers in %s\n", buf
);
1430 case TR_TRACKER_QUEUED
:
1431 printf (" Queued to ask for more peers\n");
1433 case TR_TRACKER_ACTIVE
:
1434 tr_strltime (buf
, now
- lastAnnounceStartTime
, sizeof (buf
));
1435 printf (" Asking for more peers now... %s\n", buf
);
1441 tr_strltime (buf
, now
- lastScrapeTime
, sizeof (buf
));
1442 if (lastScrapeSucceeded
)
1443 printf (" Tracker had %d seeders and %d leechers %s ago\n",
1444 (int)seederCount
, (int)leecherCount
, buf
);
1445 else if (lastScrapeTimedOut
)
1446 printf (" Tracker scrape timed out; will retry\n");
1448 printf (" Got a scrape error \"%s\" %s ago\n",
1449 lastScrapeResult
, buf
);
1452 switch (scrapeState
)
1454 case TR_TRACKER_INACTIVE
:
1456 case TR_TRACKER_WAITING
:
1457 tr_strltime (buf
, nextScrapeTime
- now
, sizeof (buf
));
1458 printf (" Asking for peer counts in %s\n", buf
);
1460 case TR_TRACKER_QUEUED
:
1461 printf (" Queued to ask for peer counts\n");
1463 case TR_TRACKER_ACTIVE
:
1464 tr_strltime (buf
, now
- lastScrapeStartTime
, sizeof (buf
));
1465 printf (" Asking for peer counts now... %s\n", buf
);
1474 printTrackers (tr_variant
* top
)
1476 tr_variant
*args
, *torrents
;
1478 if (tr_variantDictFindDict (top
, TR_KEY_arguments
, &args
)
1479 && tr_variantDictFindList (args
, TR_KEY_torrents
, &torrents
))
1482 for (i
=0, n
=tr_variantListSize (torrents
); i
<n
; ++i
)
1484 tr_variant
* trackerStats
;
1485 tr_variant
* torrent
= tr_variantListChild (torrents
, i
);
1487 if (tr_variantDictFindList (torrent
, TR_KEY_trackerStats
, &trackerStats
))
1489 printTrackersImpl (trackerStats
);
1499 printSession (tr_variant
* top
)
1502 if ((tr_variantDictFindDict (top
, TR_KEY_arguments
, &args
)))
1509 printf ("VERSION\n");
1510 if (tr_variantDictFindStr (args
, TR_KEY_version
, &str
, NULL
))
1511 printf (" Daemon version: %s\n", str
);
1512 if (tr_variantDictFindInt (args
, TR_KEY_rpc_version
, &i
))
1513 printf (" RPC version: %" PRId64
"\n", i
);
1514 if (tr_variantDictFindInt (args
, TR_KEY_rpc_version_minimum
, &i
))
1515 printf (" RPC minimum version: %" PRId64
"\n", i
);
1518 printf ("CONFIG\n");
1519 if (tr_variantDictFindStr (args
, TR_KEY_config_dir
, &str
, NULL
))
1520 printf (" Configuration directory: %s\n", str
);
1521 if (tr_variantDictFindStr (args
, TR_KEY_download_dir
, &str
, NULL
))
1522 printf (" Download directory: %s\n", str
);
1523 if (tr_variantDictFindInt (args
, TR_KEY_peer_port
, &i
))
1524 printf (" Listenport: %" PRId64
"\n", i
);
1525 if (tr_variantDictFindBool (args
, TR_KEY_port_forwarding_enabled
, &boolVal
))
1526 printf (" Portforwarding enabled: %s\n", (boolVal
? "Yes" : "No"));
1527 if (tr_variantDictFindBool (args
, TR_KEY_utp_enabled
, &boolVal
))
1528 printf (" uTP enabled: %s\n", (boolVal
? "Yes" : "No"));
1529 if (tr_variantDictFindBool (args
, TR_KEY_dht_enabled
, &boolVal
))
1530 printf (" Distributed hash table enabled: %s\n", (boolVal
? "Yes" : "No"));
1531 if (tr_variantDictFindBool (args
, TR_KEY_lpd_enabled
, &boolVal
))
1532 printf (" Local peer discovery enabled: %s\n", (boolVal
? "Yes" : "No"));
1533 if (tr_variantDictFindBool (args
, TR_KEY_pex_enabled
, &boolVal
))
1534 printf (" Peer exchange allowed: %s\n", (boolVal
? "Yes" : "No"));
1535 if (tr_variantDictFindStr (args
, TR_KEY_encryption
, &str
, NULL
))
1536 printf (" Encryption: %s\n", str
);
1537 if (tr_variantDictFindInt (args
, TR_KEY_cache_size_mb
, &i
))
1538 printf (" Maximum memory cache size: %s\n", tr_formatter_mem_MB (buf
, i
, sizeof (buf
)));
1542 bool altEnabled
, altTimeEnabled
, upEnabled
, downEnabled
, seedRatioLimited
;
1543 int64_t altDown
, altUp
, altBegin
, altEnd
, altDay
, upLimit
, downLimit
, peerLimit
;
1544 double seedRatioLimit
;
1546 if (tr_variantDictFindInt (args
, TR_KEY_alt_speed_down
, &altDown
) &&
1547 tr_variantDictFindBool (args
, TR_KEY_alt_speed_enabled
, &altEnabled
) &&
1548 tr_variantDictFindInt (args
, TR_KEY_alt_speed_time_begin
, &altBegin
) &&
1549 tr_variantDictFindBool (args
, TR_KEY_alt_speed_time_enabled
, &altTimeEnabled
) &&
1550 tr_variantDictFindInt (args
, TR_KEY_alt_speed_time_end
, &altEnd
) &&
1551 tr_variantDictFindInt (args
, TR_KEY_alt_speed_time_day
, &altDay
) &&
1552 tr_variantDictFindInt (args
, TR_KEY_alt_speed_up
, &altUp
) &&
1553 tr_variantDictFindInt (args
, TR_KEY_peer_limit_global
, &peerLimit
) &&
1554 tr_variantDictFindInt (args
, TR_KEY_speed_limit_down
, &downLimit
) &&
1555 tr_variantDictFindBool (args
, TR_KEY_speed_limit_down_enabled
, &downEnabled
) &&
1556 tr_variantDictFindInt (args
, TR_KEY_speed_limit_up
, &upLimit
) &&
1557 tr_variantDictFindBool (args
, TR_KEY_speed_limit_up_enabled
, &upEnabled
) &&
1558 tr_variantDictFindReal (args
, TR_KEY_seedRatioLimit
, &seedRatioLimit
) &&
1559 tr_variantDictFindBool (args
, TR_KEY_seedRatioLimited
, &seedRatioLimited
))
1565 printf ("LIMITS\n");
1566 printf (" Peer limit: %" PRId64
"\n", peerLimit
);
1568 if (seedRatioLimited
)
1569 strlratio2 (buf
, seedRatioLimit
, sizeof(buf
));
1571 tr_strlcpy (buf
, "Unlimited", sizeof (buf
));
1572 printf (" Default seed ratio limit: %s\n", buf
);
1575 tr_formatter_speed_KBps (buf
, altUp
, sizeof (buf
));
1577 tr_formatter_speed_KBps (buf
, upLimit
, sizeof (buf
));
1579 tr_strlcpy (buf
, "Unlimited", sizeof (buf
));
1580 printf (" Upload speed limit: %s (%s limit: %s; %s turtle limit: %s)\n",
1582 upEnabled
? "Enabled" : "Disabled",
1583 tr_formatter_speed_KBps (buf2
, upLimit
, sizeof (buf2
)),
1584 altEnabled
? "Enabled" : "Disabled",
1585 tr_formatter_speed_KBps (buf3
, altUp
, sizeof (buf3
)));
1588 tr_formatter_speed_KBps (buf
, altDown
, sizeof (buf
));
1589 else if (downEnabled
)
1590 tr_formatter_speed_KBps (buf
, downLimit
, sizeof (buf
));
1592 tr_strlcpy (buf
, "Unlimited", sizeof (buf
));
1593 printf (" Download speed limit: %s (%s limit: %s; %s turtle limit: %s)\n",
1595 downEnabled
? "Enabled" : "Disabled",
1596 tr_formatter_speed_KBps (buf2
, downLimit
, sizeof (buf2
)),
1597 altEnabled
? "Enabled" : "Disabled",
1598 tr_formatter_speed_KBps (buf3
, altDown
, sizeof (buf3
)));
1600 if (altTimeEnabled
) {
1601 printf (" Turtle schedule: %02d:%02d - %02d:%02d ",
1602 (int)(altBegin
/60), (int)(altBegin
%60),
1603 (int)(altEnd
/60), (int)(altEnd
%60));
1604 if (altDay
& TR_SCHED_SUN
) printf ("Sun ");
1605 if (altDay
& TR_SCHED_MON
) printf ("Mon ");
1606 if (altDay
& TR_SCHED_TUES
) printf ("Tue ");
1607 if (altDay
& TR_SCHED_WED
) printf ("Wed ");
1608 if (altDay
& TR_SCHED_THURS
) printf ("Thu ");
1609 if (altDay
& TR_SCHED_FRI
) printf ("Fri ");
1610 if (altDay
& TR_SCHED_SAT
) printf ("Sat ");
1618 if (tr_variantDictFindBool (args
, TR_KEY_start_added_torrents
, &boolVal
))
1619 printf (" Autostart added torrents: %s\n", (boolVal
? "Yes" : "No"));
1620 if (tr_variantDictFindBool (args
, TR_KEY_trash_original_torrent_files
, &boolVal
))
1621 printf (" Delete automatically added torrents: %s\n", (boolVal
? "Yes" : "No"));
1626 printSessionStats (tr_variant
* top
)
1628 tr_variant
*args
, *d
;
1629 if ((tr_variantDictFindDict (top
, TR_KEY_arguments
, &args
)))
1632 int64_t up
, down
, secs
, sessions
;
1634 if (tr_variantDictFindDict (args
, TR_KEY_current_stats
, &d
)
1635 && tr_variantDictFindInt (d
, TR_KEY_uploadedBytes
, &up
)
1636 && tr_variantDictFindInt (d
, TR_KEY_downloadedBytes
, &down
)
1637 && tr_variantDictFindInt (d
, TR_KEY_secondsActive
, &secs
))
1639 printf ("\nCURRENT SESSION\n");
1640 printf (" Uploaded: %s\n", strlsize (buf
, up
, sizeof (buf
)));
1641 printf (" Downloaded: %s\n", strlsize (buf
, down
, sizeof (buf
)));
1642 printf (" Ratio: %s\n", strlratio (buf
, up
, down
, sizeof (buf
)));
1643 printf (" Duration: %s\n", tr_strltime (buf
, secs
, sizeof (buf
)));
1646 if (tr_variantDictFindDict (args
, TR_KEY_cumulative_stats
, &d
)
1647 && tr_variantDictFindInt (d
, TR_KEY_sessionCount
, &sessions
)
1648 && tr_variantDictFindInt (d
, TR_KEY_uploadedBytes
, &up
)
1649 && tr_variantDictFindInt (d
, TR_KEY_downloadedBytes
, &down
)
1650 && tr_variantDictFindInt (d
, TR_KEY_secondsActive
, &secs
))
1652 printf ("\nTOTAL\n");
1653 printf (" Started %lu times\n", (unsigned long)sessions
);
1654 printf (" Uploaded: %s\n", strlsize (buf
, up
, sizeof (buf
)));
1655 printf (" Downloaded: %s\n", strlsize (buf
, down
, sizeof (buf
)));
1656 printf (" Ratio: %s\n", strlratio (buf
, up
, down
, sizeof (buf
)));
1657 printf (" Duration: %s\n", tr_strltime (buf
, secs
, sizeof (buf
)));
1662 static char id
[4096];
1665 processResponse (const char * rpcurl
, const void * response
, size_t len
)
1668 int status
= EXIT_SUCCESS
;
1671 fprintf (stderr
, "got response (len %d):\n--------\n%*.*s\n--------\n",
1672 (int)len
, (int)len
, (int)len
, (const char*) response
);
1674 if (tr_variantFromJson (&top
, response
, len
))
1676 tr_logAddNamedError (MY_NAME
, "Unable to parse response \"%*.*s\"", (int)len
,
1677 (int)len
, (char*)response
);
1678 status
|= EXIT_FAILURE
;
1685 if (tr_variantDictFindStr (&top
, TR_KEY_result
, &str
, NULL
))
1687 if (strcmp (str
, "success"))
1689 printf ("Error: %s\n", str
);
1690 status
|= EXIT_FAILURE
;
1694 tr_variantDictFindInt (&top
, TR_KEY_tag
, &tag
);
1699 printSession (&top
); break;
1702 printSessionStats (&top
); break;
1705 printDetails (&top
); break;
1708 printFileList (&top
); break;
1711 printTorrentList (&top
); break;
1714 printPeers (&top
); break;
1717 printPieces (&top
); break;
1720 printPortTest (&top
); break;
1723 printTrackers (&top
); break;
1725 case TAG_TORRENT_ADD
: {
1727 tr_variant
* b
= &top
;
1728 if (tr_variantDictFindDict (&top
, ARGUMENTS
, &b
)
1729 && tr_variantDictFindDict (b
, TR_KEY_torrent_added
, &b
)
1730 && tr_variantDictFindInt (b
, TR_KEY_id
, &i
))
1731 tr_snprintf (id
, sizeof (id
), "%"PRId64
, i
);
1732 /* fall-through to default: to give success or failure msg */
1735 if (!tr_variantDictFindStr (&top
, TR_KEY_result
, &str
, NULL
))
1736 status
|= EXIT_FAILURE
;
1738 printf ("%s responded: \"%s\"\n", rpcurl
, str
);
1739 if (strcmp (str
, "success"))
1740 status
|= EXIT_FAILURE
;
1744 tr_variantFree (&top
);
1748 status
|= EXIT_FAILURE
;
1755 tr_curl_easy_init (struct evbuffer
* writebuf
)
1757 CURL
* curl
= curl_easy_init ();
1758 curl_easy_setopt (curl
, CURLOPT_USERAGENT
, MY_NAME
"/" LONG_VERSION_STRING
);
1759 curl_easy_setopt (curl
, CURLOPT_WRITEFUNCTION
, writeFunc
);
1760 curl_easy_setopt (curl
, CURLOPT_WRITEDATA
, writebuf
);
1761 curl_easy_setopt (curl
, CURLOPT_HEADERFUNCTION
, parseResponseHeader
);
1762 curl_easy_setopt (curl
, CURLOPT_POST
, 1);
1763 curl_easy_setopt (curl
, CURLOPT_NETRC
, CURL_NETRC_OPTIONAL
);
1764 curl_easy_setopt (curl
, CURLOPT_HTTPAUTH
, CURLAUTH_ANY
);
1765 curl_easy_setopt (curl
, CURLOPT_VERBOSE
, debug
);
1766 curl_easy_setopt (curl
, CURLOPT_ENCODING
, ""); /* "" tells curl to fill in the blanks with what it was compiled to support */
1768 curl_easy_setopt (curl
, CURLOPT_NETRC_FILE
, netrc
);
1770 curl_easy_setopt (curl
, CURLOPT_USERPWD
, auth
);
1773 curl_easy_setopt (curl
, CURLOPT_SSL_VERIFYHOST
, 0); /* do not verify subject/hostname */
1774 curl_easy_setopt (curl
, CURLOPT_SSL_VERIFYPEER
, 0); /* since most certs will be self-signed, do not verify against CA */
1777 char * h
= tr_strdup_printf ("%s: %s", TR_RPC_SESSION_ID_HEADER
, sessionId
);
1778 struct curl_slist
* custom_headers
= curl_slist_append (NULL
, h
);
1779 curl_easy_setopt (curl
, CURLOPT_HTTPHEADER
, custom_headers
);
1786 flush (const char * rpcurl
, tr_variant
** benc
)
1790 int status
= EXIT_SUCCESS
;
1791 struct evbuffer
* buf
= evbuffer_new ();
1792 char * json
= tr_variantToStr (*benc
, TR_VARIANT_FMT_JSON_LEAN
, NULL
);
1793 char *rpcurl_http
= tr_strdup_printf (UseSSL
? "https://%s" : "http://%s", rpcurl
);
1795 curl
= tr_curl_easy_init (buf
);
1796 curl_easy_setopt (curl
, CURLOPT_URL
, rpcurl_http
);
1797 curl_easy_setopt (curl
, CURLOPT_POSTFIELDS
, json
);
1798 curl_easy_setopt (curl
, CURLOPT_TIMEOUT
, getTimeoutSecs (json
));
1801 fprintf (stderr
, "posting:\n--------\n%s\n--------\n", json
);
1803 if ((res
= curl_easy_perform (curl
)))
1805 tr_logAddNamedError (MY_NAME
, " (%s) %s", rpcurl_http
, curl_easy_strerror (res
));
1806 status
|= EXIT_FAILURE
;
1811 curl_easy_getinfo (curl
, CURLINFO_RESPONSE_CODE
, &response
);
1814 status
|= processResponse (rpcurl
, (const char*) evbuffer_pullup (buf
, -1), evbuffer_get_length (buf
));
1817 /* Session id failed. Our curl header func has already
1818 * pulled the new session id from this response's headers,
1819 * build a new CURL* and try again */
1820 curl_easy_cleanup (curl
);
1822 status
|= flush (rpcurl
, benc
);
1826 fprintf (stderr
, "Unexpected response: %s\n", evbuffer_pullup (buf
, -1));
1827 status
|= EXIT_FAILURE
;
1833 tr_free (rpcurl_http
);
1835 evbuffer_free (buf
);
1837 curl_easy_cleanup (curl
);
1839 tr_variantFree (*benc
);
1846 ensure_sset (tr_variant
** sset
)
1851 args
= tr_variantDictFind (*sset
, ARGUMENTS
);
1853 *sset
= tr_new0 (tr_variant
, 1);
1854 tr_variantInitDict (*sset
, 3);
1855 tr_variantDictAddStr (*sset
, TR_KEY_method
, "session-set");
1856 args
= tr_variantDictAddDict (*sset
, ARGUMENTS
, 0);
1863 ensure_tset (tr_variant
** tset
)
1868 args
= tr_variantDictFind (*tset
, ARGUMENTS
);
1870 *tset
= tr_new0 (tr_variant
, 1);
1871 tr_variantInitDict (*tset
, 3);
1872 tr_variantDictAddStr (*tset
, TR_KEY_method
, "torrent-set");
1873 args
= tr_variantDictAddDict (*tset
, ARGUMENTS
, 1);
1880 processArgs (const char * rpcurl
, int argc
, const char ** argv
)
1883 int status
= EXIT_SUCCESS
;
1884 const char * optarg
;
1885 tr_variant
*sset
= 0;
1886 tr_variant
*tset
= 0;
1887 tr_variant
*tadd
= 0;
1891 while ((c
= tr_getopt (getUsage (), argc
, argv
, opts
, &optarg
)))
1893 const int stepMode
= getOptMode (c
);
1895 if (!stepMode
) /* meta commands */
1899 case 'a': /* add torrent */
1900 if (sset
!= 0) status
|= flush (rpcurl
, &sset
);
1901 if (tadd
!= 0) status
|= flush (rpcurl
, &tadd
);
1902 if (tset
!= 0) { addIdArg (tr_variantDictFind (tset
, ARGUMENTS
), id
, NULL
); status
|= flush (rpcurl
, &tset
); }
1903 tadd
= tr_new0 (tr_variant
, 1);
1904 tr_variantInitDict (tadd
, 3);
1905 tr_variantDictAddStr (tadd
, TR_KEY_method
, "torrent-add");
1906 tr_variantDictAddInt (tadd
, TR_KEY_tag
, TAG_TORRENT_ADD
);
1907 tr_variantDictAddDict (tadd
, ARGUMENTS
, 0);
1910 case 'b': /* debug */
1914 case 'n': /* auth */
1915 auth
= tr_strdup (optarg
);
1918 case 810: /* authenv */
1920 char *authenv
= getenv ("TR_AUTH");
1922 fprintf (stderr
, "The TR_AUTH environment variable is not set\n");
1925 auth
= tr_strdup (authenv
);
1929 case 'N': /* netrc */
1930 netrc
= tr_strdup (optarg
);
1933 case 820: /* UseSSL */
1937 case 't': /* set current torrent */
1938 if (tadd
!= 0) status
|= flush (rpcurl
, &tadd
);
1939 if (tset
!= 0) { addIdArg (tr_variantDictFind (tset
, ARGUMENTS
), id
, NULL
); status
|= flush (rpcurl
, &tset
); }
1940 tr_strlcpy (id
, optarg
, sizeof (id
));
1943 case 'V': /* show version number */
1944 fprintf (stderr
, "%s %s\n", MY_NAME
, LONG_VERSION_STRING
);
1949 fprintf (stderr
, "invalid option\n");
1951 status
|= EXIT_FAILURE
;
1956 tr_variant
* args
= tr_variantDictFind (tadd
, ARGUMENTS
);
1957 char * tmp
= getEncodedMetainfo (optarg
);
1959 tr_variantDictAddStr (args
, TR_KEY_metainfo
, tmp
);
1961 tr_variantDictAddStr (args
, TR_KEY_filename
, optarg
);
1964 fprintf (stderr
, "Unknown option: %s\n", optarg
);
1965 status
|= EXIT_FAILURE
;
1970 else if (stepMode
== MODE_TORRENT_GET
)
1973 tr_variant
* top
= tr_new0 (tr_variant
, 1);
1975 tr_variant
* fields
;
1976 tr_variantInitDict (top
, 3);
1977 tr_variantDictAddStr (top
, TR_KEY_method
, "torrent-get");
1978 args
= tr_variantDictAddDict (top
, ARGUMENTS
, 0);
1979 fields
= tr_variantDictAddList (args
, TR_KEY_fields
, 0);
1981 if (tset
!= 0) { addIdArg (tr_variantDictFind (tset
, ARGUMENTS
), id
, NULL
); status
|= flush (rpcurl
, &tset
); }
1985 case 'i': tr_variantDictAddInt (top
, TR_KEY_tag
, TAG_DETAILS
);
1986 n
= TR_N_ELEMENTS (details_keys
);
1987 for (i
=0; i
<n
; ++i
) tr_variantListAddQuark (fields
, details_keys
[i
]);
1988 addIdArg (args
, id
, NULL
);
1990 case 'l': tr_variantDictAddInt (top
, TR_KEY_tag
, TAG_LIST
);
1991 n
= TR_N_ELEMENTS (list_keys
);
1992 for (i
=0; i
<n
; ++i
) tr_variantListAddQuark (fields
, list_keys
[i
]);
1993 addIdArg (args
, id
, "all");
1995 case 940: tr_variantDictAddInt (top
, TR_KEY_tag
, TAG_FILES
);
1996 n
= TR_N_ELEMENTS (files_keys
);
1997 for (i
=0; i
<n
; ++i
) tr_variantListAddQuark (fields
, files_keys
[i
]);
1998 addIdArg (args
, id
, NULL
);
2000 case 941: tr_variantDictAddInt (top
, TR_KEY_tag
, TAG_PEERS
);
2001 tr_variantListAddStr (fields
, "peers");
2002 addIdArg (args
, id
, NULL
);
2004 case 942: tr_variantDictAddInt (top
, TR_KEY_tag
, TAG_PIECES
);
2005 tr_variantListAddStr (fields
, "pieces");
2006 tr_variantListAddStr (fields
, "pieceCount");
2007 addIdArg (args
, id
, NULL
);
2009 case 943: tr_variantDictAddInt (top
, TR_KEY_tag
, TAG_TRACKERS
);
2010 tr_variantListAddStr (fields
, "trackerStats");
2011 addIdArg (args
, id
, NULL
);
2013 default: assert ("unhandled value" && 0);
2016 status
|= flush (rpcurl
, &top
);
2018 else if (stepMode
== MODE_SESSION_SET
)
2020 tr_variant
* args
= ensure_sset (&sset
);
2024 case 800: tr_variantDictAddStr (args
, TR_KEY_script_torrent_done_filename
, optarg
);
2025 tr_variantDictAddBool (args
, TR_KEY_script_torrent_done_enabled
, true);
2027 case 801: tr_variantDictAddBool (args
, TR_KEY_script_torrent_done_enabled
, false);
2029 case 970: tr_variantDictAddBool (args
, TR_KEY_alt_speed_enabled
, true);
2031 case 971: tr_variantDictAddBool (args
, TR_KEY_alt_speed_enabled
, false);
2033 case 972: tr_variantDictAddInt (args
, TR_KEY_alt_speed_down
, numarg (optarg
));
2035 case 973: tr_variantDictAddInt (args
, TR_KEY_alt_speed_up
, numarg (optarg
));
2037 case 974: tr_variantDictAddBool (args
, TR_KEY_alt_speed_time_enabled
, true);
2039 case 975: tr_variantDictAddBool (args
, TR_KEY_alt_speed_time_enabled
, false);
2041 case 976: addTime (args
, TR_KEY_alt_speed_time_begin
, optarg
);
2043 case 977: addTime (args
, TR_KEY_alt_speed_time_end
, optarg
);
2045 case 978: addDays (args
, TR_KEY_alt_speed_time_day
, optarg
);
2047 case 'c': tr_variantDictAddStr (args
, TR_KEY_incomplete_dir
, optarg
);
2048 tr_variantDictAddBool (args
, TR_KEY_incomplete_dir_enabled
, true);
2050 case 'C': tr_variantDictAddBool (args
, TR_KEY_incomplete_dir_enabled
, false);
2052 case 'e': tr_variantDictAddInt (args
, TR_KEY_cache_size_mb
, atoi (optarg
));
2054 case 910: tr_variantDictAddStr (args
, TR_KEY_encryption
, "required");
2056 case 911: tr_variantDictAddStr (args
, TR_KEY_encryption
, "preferred");
2058 case 912: tr_variantDictAddStr (args
, TR_KEY_encryption
, "tolerated");
2060 case 'm': tr_variantDictAddBool (args
, TR_KEY_port_forwarding_enabled
, true);
2062 case 'M': tr_variantDictAddBool (args
, TR_KEY_port_forwarding_enabled
, false);
2064 case 'o': tr_variantDictAddBool (args
, TR_KEY_dht_enabled
, true);
2066 case 'O': tr_variantDictAddBool (args
, TR_KEY_dht_enabled
, false);
2068 case 830: tr_variantDictAddBool (args
, TR_KEY_utp_enabled
, true);
2070 case 831: tr_variantDictAddBool (args
, TR_KEY_utp_enabled
, false);
2072 case 'p': tr_variantDictAddInt (args
, TR_KEY_peer_port
, numarg (optarg
));
2074 case 'P': tr_variantDictAddBool (args
, TR_KEY_peer_port_random_on_start
, true);
2076 case 'x': tr_variantDictAddBool (args
, TR_KEY_pex_enabled
, true);
2078 case 'X': tr_variantDictAddBool (args
, TR_KEY_pex_enabled
, false);
2080 case 'y': tr_variantDictAddBool (args
, TR_KEY_lpd_enabled
, true);
2082 case 'Y': tr_variantDictAddBool (args
, TR_KEY_lpd_enabled
, false);
2084 case 953: tr_variantDictAddReal (args
, TR_KEY_seedRatioLimit
, atof (optarg
));
2085 tr_variantDictAddBool (args
, TR_KEY_seedRatioLimited
, true);
2087 case 954: tr_variantDictAddBool (args
, TR_KEY_seedRatioLimited
, false);
2089 case 990: tr_variantDictAddBool (args
, TR_KEY_start_added_torrents
, false);
2091 case 991: tr_variantDictAddBool (args
, TR_KEY_start_added_torrents
, true);
2093 case 992: tr_variantDictAddBool (args
, TR_KEY_trash_original_torrent_files
, true);
2095 case 993: tr_variantDictAddBool (args
, TR_KEY_trash_original_torrent_files
, false);
2097 default: assert ("unhandled value" && 0);
2101 else if (stepMode
== (MODE_SESSION_SET
| MODE_TORRENT_SET
))
2103 tr_variant
* targs
= 0;
2104 tr_variant
* sargs
= 0;
2107 targs
= ensure_tset (&tset
);
2109 sargs
= ensure_sset (&sset
);
2113 case 'd': if (targs
) {
2114 tr_variantDictAddInt (targs
, TR_KEY_downloadLimit
, numarg (optarg
));
2115 tr_variantDictAddBool (targs
, TR_KEY_downloadLimited
, true);
2117 tr_variantDictAddInt (sargs
, TR_KEY_speed_limit_down
, numarg (optarg
));
2118 tr_variantDictAddBool (sargs
, TR_KEY_speed_limit_down_enabled
, true);
2121 case 'D': if (targs
)
2122 tr_variantDictAddBool (targs
, TR_KEY_downloadLimited
, false);
2124 tr_variantDictAddBool (sargs
, TR_KEY_speed_limit_down_enabled
, false);
2126 case 'u': if (targs
) {
2127 tr_variantDictAddInt (targs
, TR_KEY_uploadLimit
, numarg (optarg
));
2128 tr_variantDictAddBool (targs
, TR_KEY_uploadLimited
, true);
2130 tr_variantDictAddInt (sargs
, TR_KEY_speed_limit_up
, numarg (optarg
));
2131 tr_variantDictAddBool (sargs
, TR_KEY_speed_limit_up_enabled
, true);
2134 case 'U': if (targs
)
2135 tr_variantDictAddBool (targs
, TR_KEY_uploadLimited
, false);
2137 tr_variantDictAddBool (sargs
, TR_KEY_speed_limit_up_enabled
, false);
2139 case 930: if (targs
)
2140 tr_variantDictAddInt (targs
, TR_KEY_peer_limit
, atoi (optarg
));
2142 tr_variantDictAddInt (sargs
, TR_KEY_peer_limit_global
, atoi (optarg
));
2144 default: assert ("unhandled value" && 0);
2148 else if (stepMode
== MODE_TORRENT_SET
)
2150 tr_variant
* args
= ensure_tset (&tset
);
2154 case 712: tr_variantListAddInt (tr_variantDictAddList (args
, TR_KEY_trackerRemove
, 1), atoi (optarg
));
2156 case 950: tr_variantDictAddReal (args
, TR_KEY_seedRatioLimit
, atof (optarg
));
2157 tr_variantDictAddInt (args
, TR_KEY_seedRatioMode
, TR_RATIOLIMIT_SINGLE
);
2159 case 951: tr_variantDictAddInt (args
, TR_KEY_seedRatioMode
, TR_RATIOLIMIT_GLOBAL
);
2161 case 952: tr_variantDictAddInt (args
, TR_KEY_seedRatioMode
, TR_RATIOLIMIT_UNLIMITED
);
2163 case 984: tr_variantDictAddBool (args
, TR_KEY_honorsSessionLimits
, true);
2165 case 985: tr_variantDictAddBool (args
, TR_KEY_honorsSessionLimits
, false);
2167 default: assert ("unhandled value" && 0);
2171 else if (stepMode
== (MODE_TORRENT_SET
| MODE_TORRENT_ADD
))
2176 args
= tr_variantDictFind (tadd
, ARGUMENTS
);
2178 args
= ensure_tset (&tset
);
2182 case 'g': addFiles (args
, TR_KEY_files_wanted
, optarg
);
2184 case 'G': addFiles (args
, TR_KEY_files_unwanted
, optarg
);
2186 case 900: addFiles (args
, TR_KEY_priority_high
, optarg
);
2188 case 901: addFiles (args
, TR_KEY_priority_normal
, optarg
);
2190 case 902: addFiles (args
, TR_KEY_priority_low
, optarg
);
2192 case 700: tr_variantDictAddInt (args
, TR_KEY_bandwidthPriority
, 1);
2194 case 701: tr_variantDictAddInt (args
, TR_KEY_bandwidthPriority
, 0);
2196 case 702: tr_variantDictAddInt (args
, TR_KEY_bandwidthPriority
, -1);
2198 case 710: tr_variantListAddStr (tr_variantDictAddList (args
, TR_KEY_trackerAdd
, 1), optarg
);
2200 default: assert ("unhandled value" && 0);
2204 else if (c
== 961) /* set location */
2208 tr_variant
* args
= tr_variantDictFind (tadd
, ARGUMENTS
);
2209 tr_variantDictAddStr (args
, TR_KEY_download_dir
, optarg
);
2214 tr_variant
* top
= tr_new0 (tr_variant
, 1);
2215 tr_variantInitDict (top
, 2);
2216 tr_variantDictAddStr (top
, TR_KEY_method
, "torrent-set-location");
2217 args
= tr_variantDictAddDict (top
, ARGUMENTS
, 3);
2218 tr_variantDictAddStr (args
, TR_KEY_location
, optarg
);
2219 tr_variantDictAddBool (args
, TR_KEY_move
, false);
2220 addIdArg (args
, id
, NULL
);
2221 status
|= flush (rpcurl
, &top
);
2227 case 920: /* session-info */
2229 tr_variant
* top
= tr_new0 (tr_variant
, 1);
2230 tr_variantInitDict (top
, 2);
2231 tr_variantDictAddStr (top
, TR_KEY_method
, "session-get");
2232 tr_variantDictAddInt (top
, TR_KEY_tag
, TAG_SESSION
);
2233 status
|= flush (rpcurl
, &top
);
2236 case 's': /* start */
2239 tr_variantDictAddBool (tr_variantDictFind (tadd
, TR_KEY_arguments
), TR_KEY_paused
, false);
2241 tr_variant
* top
= tr_new0 (tr_variant
, 1);
2242 tr_variantInitDict (top
, 2);
2243 tr_variantDictAddStr (top
, TR_KEY_method
, "torrent-start");
2244 addIdArg (tr_variantDictAddDict (top
, ARGUMENTS
, 1), id
, NULL
);
2245 status
|= flush (rpcurl
, &top
);
2249 case 'S': /* stop */
2252 tr_variantDictAddBool (tr_variantDictFind (tadd
, TR_KEY_arguments
), TR_KEY_paused
, true);
2254 tr_variant
* top
= tr_new0 (tr_variant
, 1);
2255 tr_variantInitDict (top
, 2);
2256 tr_variantDictAddStr (top
, TR_KEY_method
, "torrent-stop");
2257 addIdArg (tr_variantDictAddDict (top
, ARGUMENTS
, 1), id
, NULL
);
2258 status
|= flush (rpcurl
, &top
);
2264 char * path
= absolutify (optarg
);
2266 tr_variantDictAddStr (tr_variantDictFind (tadd
, TR_KEY_arguments
), TR_KEY_download_dir
, path
);
2268 tr_variant
* args
= ensure_sset (&sset
);
2269 tr_variantDictAddStr (args
, TR_KEY_download_dir
, path
);
2276 tr_variant
* top
= tr_new0 (tr_variant
, 1);
2277 tr_variantInitDict (top
, 1);
2278 tr_variantDictAddStr (top
, TR_KEY_method
, "session-close");
2279 status
|= flush (rpcurl
, &top
);
2284 tr_variant
* top
= tr_new0 (tr_variant
, 1);
2285 tr_variantInitDict (top
, 1);
2286 tr_variantDictAddStr (top
, TR_KEY_method
, "blocklist-update");
2287 status
|= flush (rpcurl
, &top
);
2292 tr_variant
* top
= tr_new0 (tr_variant
, 1);
2293 tr_variantInitDict (top
, 2);
2294 tr_variantDictAddStr (top
, TR_KEY_method
, "session-stats");
2295 tr_variantDictAddInt (top
, TR_KEY_tag
, TAG_STATS
);
2296 status
|= flush (rpcurl
, &top
);
2301 tr_variant
* top
= tr_new0 (tr_variant
, 1);
2302 tr_variantInitDict (top
, 2);
2303 tr_variantDictAddStr (top
, TR_KEY_method
, "port-test");
2304 tr_variantDictAddInt (top
, TR_KEY_tag
, TAG_PORTTEST
);
2305 status
|= flush (rpcurl
, &top
);
2311 if (tset
!= 0) { addIdArg (tr_variantDictFind (tset
, ARGUMENTS
), id
, NULL
); status
|= flush (rpcurl
, &tset
); }
2312 top
= tr_new0 (tr_variant
, 1);
2313 tr_variantInitDict (top
, 2);
2314 tr_variantDictAddStr (top
, TR_KEY_method
, "torrent-reannounce");
2315 addIdArg (tr_variantDictAddDict (top
, ARGUMENTS
, 1), id
, NULL
);
2316 status
|= flush (rpcurl
, &top
);
2322 if (tset
!= 0) { addIdArg (tr_variantDictFind (tset
, ARGUMENTS
), id
, NULL
); status
|= flush (rpcurl
, &tset
); }
2323 top
= tr_new0 (tr_variant
, 1);
2324 tr_variantInitDict (top
, 2);
2325 tr_variantDictAddStr (top
, TR_KEY_method
, "torrent-verify");
2326 addIdArg (tr_variantDictAddDict (top
, ARGUMENTS
, 1), id
, NULL
);
2327 status
|= flush (rpcurl
, &top
);
2334 tr_variant
* top
= tr_new0 (tr_variant
, 1);
2335 tr_variantInitDict (top
, 2);
2336 tr_variantDictAddStr (top
, TR_KEY_method
, "torrent-remove");
2337 args
= tr_variantDictAddDict (top
, ARGUMENTS
, 2);
2338 tr_variantDictAddBool (args
, TR_KEY_delete_local_data
, c
=='R');
2339 addIdArg (args
, id
, NULL
);
2340 status
|= flush (rpcurl
, &top
);
2346 tr_variant
* top
= tr_new0 (tr_variant
, 1);
2347 tr_variantInitDict (top
, 2);
2348 tr_variantDictAddStr (top
, TR_KEY_method
, "torrent-set-location");
2349 args
= tr_variantDictAddDict (top
, ARGUMENTS
, 3);
2350 tr_variantDictAddStr (args
, TR_KEY_location
, optarg
);
2351 tr_variantDictAddBool (args
, TR_KEY_move
, true);
2352 addIdArg (args
, id
, NULL
);
2353 status
|= flush (rpcurl
, &top
);
2358 fprintf (stderr
, "got opt [%d]\n", c
);
2365 if (tadd
!= 0) status
|= flush (rpcurl
, &tadd
);
2366 if (tset
!= 0) { addIdArg (tr_variantDictFind (tset
, ARGUMENTS
), id
, NULL
); status
|= flush (rpcurl
, &tset
); }
2367 if (sset
!= 0) status
|= flush (rpcurl
, &sset
);
2371 /* [host:port] or [host] or [port] or [http (s?)://host:port/transmission/] */
2373 getHostAndPortAndRpcUrl (int * argc
, char ** argv
,
2374 char ** host
, int * port
, char ** rpcurl
)
2376 if (*argv
[1] != '-')
2379 const char * s
= argv
[1];
2380 const char * delim
= strchr (s
, ':');
2381 if (!strncmp (s
, "http://", 7)) /* user passed in http rpc url */
2383 *rpcurl
= tr_strdup_printf ("%s/rpc/", s
+ 7);
2385 else if (!strncmp (s
, "https://", 8)) /* user passed in https rpc url */
2388 *rpcurl
= tr_strdup_printf ("%s/rpc/", s
+ 8);
2390 else if (delim
) /* user passed in both host and port */
2392 *host
= tr_strndup (s
, delim
- s
);
2393 *port
= atoi (delim
+ 1);
2398 const int i
= strtol (s
, &end
, 10);
2399 if (!*end
) /* user passed in a port */
2401 else /* user passed in a host */
2402 *host
= tr_strdup (s
);
2406 for (i
= 1; i
< *argc
; ++i
)
2407 argv
[i
] = argv
[i
+ 1];
2412 main (int argc
, char ** argv
)
2414 int port
= DEFAULT_PORT
;
2416 char * rpcurl
= NULL
;
2417 int exit_status
= EXIT_SUCCESS
;
2421 return EXIT_FAILURE
;
2424 tr_formatter_mem_init (MEM_K
, MEM_K_STR
, MEM_M_STR
, MEM_G_STR
, MEM_T_STR
);
2425 tr_formatter_size_init (DISK_K
,DISK_K_STR
, DISK_M_STR
, DISK_G_STR
, DISK_T_STR
);
2426 tr_formatter_speed_init (SPEED_K
, SPEED_K_STR
, SPEED_M_STR
, SPEED_G_STR
, SPEED_T_STR
);
2428 getHostAndPortAndRpcUrl (&argc
, argv
, &host
, &port
, &rpcurl
);
2430 host
= tr_strdup (DEFAULT_HOST
);
2432 rpcurl
= tr_strdup_printf ("%s:%d%s", host
, port
, DEFAULT_URL
);
2434 exit_status
= processArgs (rpcurl
, argc
, (const char**)argv
);