2 * This file Copyright (C) Mnemosyne LLC
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2
6 * as published by the Free Software Foundation.
8 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10 * $Id: remote.c 12032 2011-02-24 15:38:58Z jordan $
14 #include <ctype.h> /* isspace */
19 #include <string.h> /* strcmp */
22 #include <direct.h> /* getcwd */
24 #include <unistd.h> /* getcwd */
27 #include <event2/buffer.h>
29 #define CURL_DISABLE_TYPECHECK /* otherwise -Wunreachable-code goes insane */
30 #include <curl/curl.h>
32 #include <libtransmission/transmission.h>
33 #include <libtransmission/bencode.h>
34 #include <libtransmission/rpcimpl.h>
35 #include <libtransmission/json.h>
36 #include <libtransmission/tr-getopt.h>
37 #include <libtransmission/utils.h>
38 #include <libtransmission/version.h>
40 #define MY_NAME "transmission-remote"
41 #define DEFAULT_HOST "localhost"
42 #define DEFAULT_PORT atoi(TR_DEFAULT_RPC_PORT_STR)
43 #define DEFAULT_URL TR_DEFAULT_RPC_URL_STR "rpc/"
45 #define ARGUMENTS "arguments"
49 #define MEM_K_STR "KiB"
50 #define MEM_M_STR "MiB"
51 #define MEM_G_STR "GiB"
52 #define MEM_T_STR "TiB"
55 #define DISK_B_STR "B"
56 #define DISK_K_STR "KiB"
57 #define DISK_M_STR "MiB"
58 #define DISK_G_STR "GiB"
59 #define DISK_T_STR "TiB"
62 #define SPEED_B_STR "B/s"
63 #define SPEED_K_STR "KiB/s"
64 #define SPEED_M_STR "MiB/s"
65 #define SPEED_G_STR "GiB/s"
66 #define SPEED_T_STR "TiB/s"
70 **** Display Utilities
75 etaToString( char * buf
, size_t buflen
, int64_t eta
)
78 tr_snprintf( buf
, buflen
, "Unknown" );
80 tr_snprintf( buf
, buflen
, "%" PRId64
"sec", eta
);
81 else if( eta
< ( 60 * 60 ) )
82 tr_snprintf( buf
, buflen
, "%" PRId64
" min", eta
/ 60 );
83 else if( eta
< ( 60 * 60 * 24 ) )
84 tr_snprintf( buf
, buflen
, "%" PRId64
" hrs", eta
/ ( 60 * 60 ) );
86 tr_snprintf( buf
, buflen
, "%" PRId64
" days", eta
/ ( 60 * 60 * 24 ) );
90 tr_strltime( char * buf
, int seconds
, size_t buflen
)
92 int days
, hours
, minutes
;
93 char d
[128], h
[128], m
[128], s
[128];
98 days
= seconds
/ 86400;
99 hours
= ( seconds
% 86400 ) / 3600;
100 minutes
= ( seconds
% 3600 ) / 60;
101 seconds
= ( seconds
% 3600 ) % 60;
103 tr_snprintf( d
, sizeof( d
), "%d %s", days
, days
==1?"day":"days" );
104 tr_snprintf( h
, sizeof( h
), "%d %s", hours
, hours
==1?"hour":"hours" );
105 tr_snprintf( m
, sizeof( m
), "%d %s", minutes
, minutes
==1?"minute":"minutes" );
106 tr_snprintf( s
, sizeof( s
), "%d %s", seconds
, seconds
==1?"seconds":"seconds" );
110 if( days
>= 4 || !hours
)
111 tr_strlcpy( buf
, d
, buflen
);
113 tr_snprintf( buf
, buflen
, "%s, %s", d
, h
);
117 if( hours
>= 4 || !minutes
)
118 tr_strlcpy( buf
, h
, buflen
);
120 tr_snprintf( buf
, buflen
, "%s, %s", h
, m
);
124 if( minutes
>= 4 || !seconds
)
125 tr_strlcpy( buf
, m
, buflen
);
127 tr_snprintf( buf
, buflen
, "%s, %s", m
, s
);
129 else tr_strlcpy( buf
, s
, buflen
);
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://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 { 'o', "dht", "Enable distributed hash tables (DHT)", "o", 0, NULL
},
267 { 'O', "no-dht", "Disable distributed hash tables (DHT)", "O", 0, NULL
},
268 { 'p', "port", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR
")", "p", 1, "<port>" },
269 { 962, "port-test", "Port testing", "pt", 0, NULL
},
270 { 'P', "random-port", "Random port for incomping peers", "P", 0, NULL
},
271 { 900, "priority-high", "Try to download these file(s) first", "ph", 1, "<files>" },
272 { 901, "priority-normal", "Try to download these file(s) normally", "pn", 1, "<files>" },
273 { 902, "priority-low", "Try to download these file(s) last", "pl", 1, "<files>" },
274 { 700, "bandwidth-high", "Give this torrent first chance at available bandwidth", "Bh", 0, NULL
},
275 { 701, "bandwidth-normal", "Give this torrent bandwidth left over by high priority torrents", "Bn", 0, NULL
},
276 { 702, "bandwidth-low", "Give this torrent bandwidth left over by high and normal priority torrents", "Bl", 0, NULL
},
277 { 600, "reannounce", "Reannounce the current torrent(s)", NULL
, 0, NULL
},
278 { 'r', "remove", "Remove the current torrent(s)", "r", 0, NULL
},
279 { 930, "peers", "Set the maximum number of peers for the current torrent(s) or globally", "pr", 1, "<max>" },
280 { 'R', "remove-and-delete", "Remove the current torrent(s) and delete local data", NULL
, 0, NULL
},
281 { 800, "torrent-done-script", "Specify a script to run when a torrent finishes", NULL
, 1, "<file>" },
282 { 801, "no-torrent-done-script", "Don't run a script when torrents finish", NULL
, 0, NULL
},
283 { 950, "seedratio", "Let the current torrent(s) seed until a specific ratio", "sr", 1, "ratio" },
284 { 951, "seedratio-default", "Let the current torrent(s) use the global seedratio settings", "srd", 0, NULL
},
285 { 952, "no-seedratio", "Let the current torrent(s) seed regardless of ratio", "SR", 0, NULL
},
286 { 953, "global-seedratio", "All torrents, unless overridden by a per-torrent setting, should seed until a specific ratio", "gsr", 1, "ratio" },
287 { 954, "no-global-seedratio", "All torrents, unless overridden by a per-torrent setting, should seed regardless of ratio", "GSR", 0, NULL
},
288 { 710, "tracker-add", "Add a tracker to a torrent", "td", 1, "<tracker>" },
289 { 712, "tracker-remove", "Remove a tracker from a torrent", "tr", 1, "<trackerId>" },
290 { 's', "start", "Start the current torrent(s)", "s", 0, NULL
},
291 { 'S', "stop", "Stop the current torrent(s)", "S", 0, NULL
},
292 { 't', "torrent", "Set the current torrent(s)", "t", 1, "<torrent>" },
293 { 990, "start-paused", "Start added torrents paused", NULL
, 0, NULL
},
294 { 991, "no-start-paused", "Start added torrents unpaused", NULL
, 0, NULL
},
295 { 992, "trash-torrent", "Delete torrents after adding", NULL
, 0, NULL
},
296 { 993, "no-trash-torrent", "Do not delete torrents after adding", NULL
, 0, NULL
},
297 { 984, "honor-session", "Make the current torrent(s) honor the session limits", "hl", 0, NULL
},
298 { 985, "no-honor-session", "Make the current torrent(s) not honor the session limits", "HL", 0, NULL
},
299 { 'u', "uplimit", "Set the max upload speed in "SPEED_K_STR
" for the current torrent(s) or globally", "u", 1, "<speed>" },
300 { 'U', "no-uplimit", "Disable max upload speed for the current torrent(s) or globally", "U", 0, NULL
},
301 { 'v', "verify", "Verify the current torrent(s)", "v", 0, NULL
},
302 { 'V', "version", "Show version number and exit", "V", 0, NULL
},
303 { 'w', "download-dir", "When adding a new torrent, set its download folder. Otherwise, set the default download folder", "w", 1, "<path>" },
304 { 'x', "pex", "Enable peer exchange (PEX)", "x", 0, NULL
},
305 { 'X', "no-pex", "Disable peer exchange (PEX)", "X", 0, NULL
},
306 { 'y', "lpd", "Enable local peer discovery (LPD)", "y", 0, NULL
},
307 { 'Y', "no-lpd", "Disable local peer discovery (LPD)", "Y", 0, NULL
},
308 { 941, "peer-info", "List the current torrent(s)' peers", "pi", 0, NULL
},
309 { 0, NULL
, NULL
, NULL
, 0, NULL
}
315 tr_getopt_usage( MY_NAME
, getUsage( ), opts
);
319 numarg( const char * arg
)
322 const long num
= strtol( arg
, &end
, 10 );
326 fprintf( stderr
, "Not a number: \"%s\"\n", arg
);
328 exit( EXIT_FAILURE
);
335 MODE_TORRENT_START
= (1<<0),
336 MODE_TORRENT_STOP
= (1<<1),
337 MODE_TORRENT_VERIFY
= (1<<2),
338 MODE_TORRENT_REANNOUNCE
= (1<<3),
339 MODE_TORRENT_SET
= (1<<4),
340 MODE_TORRENT_GET
= (1<<5),
341 MODE_TORRENT_ADD
= (1<<6),
342 MODE_TORRENT_REMOVE
= (1<<7),
343 MODE_TORRENT_SET_LOCATION
= (1<<8),
344 MODE_SESSION_SET
= (1<<9),
345 MODE_SESSION_GET
= (1<<10),
346 MODE_SESSION_STATS
= (1<<11),
347 MODE_SESSION_CLOSE
= (1<<12),
348 MODE_BLOCKLIST_UPDATE
= (1<<13),
349 MODE_PORT_TEST
= (1<<14)
353 getOptMode( int val
)
359 case 'a': /* add torrent */
360 case 'b': /* debug */
362 case 810: /* authenv */
363 case 'N': /* netrc */
364 case 't': /* set current torrent */
365 case 'V': /* show version number */
368 case 'c': /* incomplete-dir */
369 case 'C': /* no-incomplete-dir */
370 case 'e': /* cache */
371 case 'm': /* portmap */
372 case 'M': /* "no-portmap */
374 case 'O': /* no-dht */
375 case 'p': /* incoming peer port */
376 case 'P': /* random incoming peer port */
378 case 'X': /* no-pex */
380 case 'Y': /* no-lpd */
381 case 800: /* torrent-done-script */
382 case 801: /* no-torrent-done-script */
383 case 970: /* alt-speed */
384 case 971: /* no-alt-speed */
385 case 972: /* alt-speed-downlimit */
386 case 973: /* alt-speed-uplimit */
387 case 974: /* alt-speed-scheduler */
388 case 975: /* no-alt-speed-scheduler */
389 case 976: /* alt-speed-time-begin */
390 case 977: /* alt-speed-time-end */
391 case 978: /* alt-speed-days */
392 case 910: /* encryption-required */
393 case 911: /* encryption-preferred */
394 case 912: /* encryption-tolerated */
395 case 953: /* global-seedratio */
396 case 954: /* no-global-seedratio */
397 case 990: /* start-paused */
398 case 991: /* no-start-paused */
399 case 992: /* trash-torrent */
400 case 993: /* no-trash-torrent */
401 return MODE_SESSION_SET
;
403 case 712: /* tracker-remove */
404 case 950: /* seedratio */
405 case 951: /* seedratio-default */
406 case 952: /* no-seedratio */
407 case 984: /* honor-session */
408 case 985: /* no-honor-session */
409 return MODE_TORRENT_SET
;
411 case 920: /* session-info */
412 return MODE_SESSION_GET
;
415 case 'G': /* no-get */
416 case 700: /* torrent priority-high */
417 case 701: /* torrent priority-normal */
418 case 702: /* torrent priority-low */
419 case 710: /* tracker-add */
420 case 900: /* file priority-high */
421 case 901: /* file priority-normal */
422 case 902: /* file priority-low */
423 return MODE_TORRENT_SET
| MODE_TORRENT_ADD
;
426 return MODE_TORRENT_SET_LOCATION
| MODE_TORRENT_ADD
;
429 case 'l': /* list all torrents */
430 case 940: /* info-files */
431 case 941: /* info-peer */
432 case 942: /* info-pieces */
433 case 943: /* info-tracker */
434 return MODE_TORRENT_GET
;
436 case 'd': /* download speed limit */
437 case 'D': /* no download speed limit */
438 case 'u': /* upload speed limit */
439 case 'U': /* no upload speed limit */
440 case 930: /* peers */
441 return MODE_SESSION_SET
| MODE_TORRENT_SET
;
443 case 's': /* start */
444 return MODE_TORRENT_START
| MODE_TORRENT_ADD
;
447 return MODE_TORRENT_STOP
| MODE_TORRENT_ADD
;
449 case 'w': /* download-dir */
450 return MODE_SESSION_SET
| MODE_TORRENT_ADD
;
452 case 850: /* session-close */
453 return MODE_SESSION_CLOSE
;
455 case 963: /* blocklist-update */
456 return MODE_BLOCKLIST_UPDATE
;
458 case 921: /* session-stats */
459 return MODE_SESSION_STATS
;
461 case 'v': /* verify */
462 return MODE_TORRENT_VERIFY
;
464 case 600: /* reannounce */
465 return MODE_TORRENT_REANNOUNCE
;
467 case 962: /* port-test */
468 return MODE_PORT_TEST
;
470 case 'r': /* remove */
471 case 'R': /* remove and delete */
472 return MODE_TORRENT_REMOVE
;
475 return MODE_TORRENT_SET_LOCATION
;
478 fprintf( stderr
, "unrecognized argument %d\n", val
);
479 assert( "unrecognized argument" && 0 );
484 static tr_bool debug
= 0;
485 static char * auth
= NULL
;
486 static char * netrc
= NULL
;
487 static char * sessionId
= NULL
;
496 result
= _getcwd( buf
, sizeof( buf
) );
498 result
= getcwd( buf
, sizeof( buf
) );
501 fprintf( stderr
, "getcwd error: \"%s\"", tr_strerror( errno
) );
502 return tr_strdup( buf
);
506 absolutify( const char * path
)
511 buf
= tr_strdup( path
);
513 char * cwd
= tr_getcwd( );
514 buf
= tr_buildPath( cwd
, path
, NULL
);
522 getEncodedMetainfo( const char * filename
)
526 uint8_t * buf
= tr_loadFile( filename
, &len
);
530 b64
= tr_base64_encode( buf
, len
, NULL
);
537 addIdArg( tr_benc
* args
, const char * id
)
543 "No torrent specified! Please use the -t option first.\n" );
544 id
= "-1"; /* no torrent will have this ID, so should be a no-op */
546 if( strcmp( id
, "all" ) )
549 tr_bool isList
= strchr(id
,',') || strchr(id
,'-');
550 tr_bool isNum
= TRUE
;
551 for( pch
=id
; isNum
&& *pch
; ++pch
)
552 if( !isdigit( *pch
) )
554 if( isNum
|| isList
)
555 tr_rpc_parse_list_str( tr_bencDictAdd( args
, "ids" ), id
, strlen( id
) );
557 tr_bencDictAddStr( args
, "ids", id
); /* it's a torrent sha hash */
562 addTime( tr_benc
* args
, const char * key
, const char * arg
)
565 tr_bool success
= FALSE
;
567 if( arg
&& ( strlen( arg
) == 4 ) )
569 const char hh
[3] = { arg
[0], arg
[1], '\0' };
570 const char mm
[3] = { arg
[2], arg
[3], '\0' };
571 const int hour
= atoi( hh
);
572 const int min
= atoi( mm
);
574 if( 0<=hour
&& hour
<24 && 0<=min
&& min
<60 )
576 time
= min
+ ( hour
* 60 );
582 tr_bencDictAddInt( args
, key
, time
);
584 fprintf( stderr
, "Please specify the time of day in 'hhmm' format.\n" );
588 addDays( tr_benc
* args
, const char * key
, const char * arg
)
596 int * values
= tr_parseNumberRange( arg
, -1, &valueCount
);
597 for( i
=0; i
<valueCount
; ++i
)
599 if ( values
[i
] < 0 || values
[i
] > 7 ) continue;
600 if ( values
[i
] == 7 ) values
[i
] = 0;
602 days
|= 1 << values
[i
];
608 tr_bencDictAddInt( args
, key
, days
);
610 fprintf( stderr
, "Please specify the days of the week in '1-3,4,7' format.\n" );
614 addFiles( tr_benc
* args
,
618 tr_benc
* files
= tr_bencDictAddList( args
, key
, 100 );
622 fprintf( stderr
, "No files specified!\n" );
623 arg
= "-1"; /* no file will have this index, so should be a no-op */
625 if( strcmp( arg
, "all" ) )
629 int * values
= tr_parseNumberRange( arg
, -1, &valueCount
);
630 for( i
=0; i
<valueCount
; ++i
)
631 tr_bencListAddInt( files
, values
[i
] );
636 #define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) )
638 static const char * files_keys
[] = {
645 static const char * details_keys
[] = {
665 "honorsSessionLimits",
672 "peersGettingFromUs",
680 "secondsDownloading",
692 "webseedsSendingToUs"
695 static const char * list_keys
[] = {
703 "peersGettingFromUs",
713 writeFunc( void * ptr
, size_t size
, size_t nmemb
, void * buf
)
715 const size_t byteCount
= size
* nmemb
;
716 evbuffer_add( buf
, ptr
, byteCount
);
720 /* look for a session id in the header in case the server gives back a 409 */
722 parseResponseHeader( void *ptr
, size_t size
, size_t nmemb
, void * stream UNUSED
)
724 const char * line
= ptr
;
725 const size_t line_len
= size
* nmemb
;
726 const char * key
= TR_RPC_SESSION_ID_HEADER
": ";
727 const size_t key_len
= strlen( key
);
729 if( ( line_len
>= key_len
) && !memcmp( line
, key
, key_len
) )
731 const char * begin
= line
+ key_len
;
732 const char * end
= begin
;
733 while( !isspace( *end
) )
735 tr_free( sessionId
);
736 sessionId
= tr_strndup( begin
, end
-begin
);
743 getTimeoutSecs( const char * req
)
745 if( strstr( req
, "\"method\":\"blocklist-update\"" ) != NULL
)
748 return 60L; /* default value */
752 getStatusString( tr_benc
* t
, char * buf
, size_t buflen
)
757 if( !tr_bencDictFindInt( t
, "status", &status
) )
761 else switch( status
)
763 case TR_STATUS_STOPPED
:
764 if( tr_bencDictFindBool( t
, "isFinished", &boolVal
) && boolVal
)
765 tr_strlcpy( buf
, "Finished", buflen
);
767 tr_strlcpy( buf
, "Stopped", buflen
);
770 case TR_STATUS_CHECK_WAIT
:
771 case TR_STATUS_CHECK
: {
772 const char * str
= status
== TR_STATUS_CHECK_WAIT
776 if( tr_bencDictFindReal( t
, "recheckProgress", &percent
) )
777 tr_snprintf( buf
, buflen
, "%s (%.0f%%)", str
, floor(percent
*100.0) );
779 tr_strlcpy( buf
, str
, buflen
);
784 case TR_STATUS_DOWNLOAD
:
785 case TR_STATUS_SEED
: {
788 tr_bencDictFindInt( t
, "peersGettingFromUs", &fromUs
);
789 tr_bencDictFindInt( t
, "peersSendingToUs", &toUs
);
791 tr_strlcpy( buf
, "Up & Down", buflen
);
793 tr_strlcpy( buf
, "Downloading", buflen
);
795 int64_t leftUntilDone
= 0;
796 tr_bencDictFindInt( t
, "leftUntilDone", &leftUntilDone
);
797 if( leftUntilDone
> 0 )
798 tr_strlcpy( buf
, "Uploading", buflen
);
800 tr_strlcpy( buf
, "Seeding", buflen
);
802 tr_strlcpy( buf
, "Idle", buflen
);
811 static const char *bandwidthPriorityNames
[] =
812 { "Low", "Normal", "High", "Invalid" };
815 printDetails( tr_benc
* top
)
817 tr_benc
*args
, *torrents
;
819 if( ( tr_bencDictFindDict( top
, "arguments", &args
) )
820 && ( tr_bencDictFindList( args
, "torrents", &torrents
) ) )
823 for( ti
= 0, tCount
= tr_bencListSize( torrents
); ti
< tCount
;
826 tr_benc
* t
= tr_bencListChild( torrents
, ti
);
836 if( tr_bencDictFindInt( t
, "id", &i
) )
837 printf( " Id: %" PRId64
"\n", i
);
838 if( tr_bencDictFindStr( t
, "name", &str
) )
839 printf( " Name: %s\n", str
);
840 if( tr_bencDictFindStr( t
, "hashString", &str
) )
841 printf( " Hash: %s\n", str
);
844 printf( "TRANSFER\n" );
845 getStatusString( t
, buf
, sizeof( buf
) );
846 printf( " State: %s\n", buf
);
848 if( tr_bencDictFindStr( t
, "downloadDir", &str
) )
849 printf( " Location: %s\n", str
);
851 if( tr_bencDictFindInt( t
, "sizeWhenDone", &i
)
852 && tr_bencDictFindInt( t
, "leftUntilDone", &j
) )
854 strlpercent( buf
, 100.0 * ( i
- j
) / i
, sizeof( buf
) );
855 printf( " Percent Done: %s%%\n", buf
);
858 if( tr_bencDictFindInt( t
, "eta", &i
) )
859 printf( " ETA: %s\n", tr_strltime( buf
, i
, sizeof( buf
) ) );
860 if( tr_bencDictFindInt( t
, "rateDownload", &i
) )
861 printf( " Download Speed: %s\n", tr_formatter_speed_KBps( buf
, i
/(double)tr_speed_K
, sizeof( buf
) ) );
862 if( tr_bencDictFindInt( t
, "rateUpload", &i
) )
863 printf( " Upload Speed: %s\n", tr_formatter_speed_KBps( buf
, i
/(double)tr_speed_K
, sizeof( buf
) ) );
864 if( tr_bencDictFindInt( t
, "haveUnchecked", &i
)
865 && tr_bencDictFindInt( t
, "haveValid", &j
) )
867 strlsize( buf
, i
+ j
, sizeof( buf
) );
868 strlsize( buf2
, j
, sizeof( buf2
) );
869 printf( " Have: %s (%s verified)\n", buf
, buf2
);
872 if( tr_bencDictFindInt( t
, "sizeWhenDone", &i
) )
875 printf( " Availability: None\n" );
876 if( tr_bencDictFindInt( t
, "desiredAvailable", &j
)
877 && tr_bencDictFindInt( t
, "leftUntilDone", &k
) )
880 strlpercent( buf
, 100.0 * j
/ i
, sizeof( buf
) );
881 printf( " Availability: %s%%\n", buf
);
883 if( tr_bencDictFindInt( t
, "totalSize", &j
) )
885 strlsize( buf2
, i
, sizeof( buf2
) );
886 strlsize( buf
, j
, sizeof( buf
) );
887 printf( " Total size: %s (%s wanted)\n", buf
, buf2
);
890 if( tr_bencDictFindInt( t
, "downloadedEver", &i
)
891 && tr_bencDictFindInt( t
, "uploadedEver", &j
) )
893 strlsize( buf
, i
, sizeof( buf
) );
894 printf( " Downloaded: %s\n", buf
);
895 strlsize( buf
, j
, sizeof( buf
) );
896 printf( " Uploaded: %s\n", buf
);
897 strlratio( buf
, j
, i
, sizeof( buf
) );
898 printf( " Ratio: %s\n", buf
);
900 if( tr_bencDictFindInt( t
, "seedRatioMode", &i
))
903 case TR_RATIOLIMIT_GLOBAL
:
904 printf( " Ratio Limit: Default\n" );
906 case TR_RATIOLIMIT_SINGLE
:
907 if( tr_bencDictFindReal( t
, "seedRatioLimit", &d
))
908 printf( " Ratio Limit: %.2f\n", d
);
910 case TR_RATIOLIMIT_UNLIMITED
:
911 printf( " Ratio Limit: Unlimited\n" );
916 if( tr_bencDictFindInt( t
, "corruptEver", &i
) )
918 strlsize( buf
, i
, sizeof( buf
) );
919 printf( " Corrupt DL: %s\n", buf
);
921 if( tr_bencDictFindStr( t
, "errorString", &str
) && str
&& *str
&&
922 tr_bencDictFindInt( t
, "error", &i
) && i
)
925 case TR_STAT_TRACKER_WARNING
: printf( " Tracker gave a warning: %s\n", str
); break;
926 case TR_STAT_TRACKER_ERROR
: printf( " Tracker gave an error: %s\n", str
); break;
927 case TR_STAT_LOCAL_ERROR
: printf( " Error: %s\n", str
); break;
928 default: break; /* no error */
931 if( tr_bencDictFindInt( t
, "peersConnected", &i
)
932 && tr_bencDictFindInt( t
, "peersGettingFromUs", &j
)
933 && tr_bencDictFindInt( t
, "peersSendingToUs", &k
) )
937 "connected to %" PRId64
", "
938 "uploading to %" PRId64
945 if( tr_bencDictFindList( t
, "webseeds", &l
)
946 && tr_bencDictFindInt( t
, "webseedsSendingToUs", &i
) )
948 const int64_t n
= tr_bencListSize( l
);
951 " Web Seeds: downloading from %" PRId64
" of %"
953 " web seeds\n", i
, n
);
957 printf( "HISTORY\n" );
958 if( tr_bencDictFindInt( t
, "addedDate", &i
) && i
)
961 printf( " Date added: %s", ctime( &tt
) );
963 if( tr_bencDictFindInt( t
, "doneDate", &i
) && i
)
966 printf( " Date finished: %s", ctime( &tt
) );
968 if( tr_bencDictFindInt( t
, "startDate", &i
) && i
)
971 printf( " Date started: %s", ctime( &tt
) );
973 if( tr_bencDictFindInt( t
, "activityDate", &i
) && i
)
976 printf( " Latest activity: %s", ctime( &tt
) );
978 if( tr_bencDictFindInt( t
, "secondsDownloading", &i
) && ( i
> 0 ) )
979 printf( " Downloading Time: %s\n", tr_strltime( buf
, i
, sizeof( buf
) ) );
980 if( tr_bencDictFindInt( t
, "secondsSeeding", &i
) && ( i
> 0 ) )
981 printf( " Seeding Time: %s\n", tr_strltime( buf
, i
, sizeof( buf
) ) );
984 printf( "ORIGINS\n" );
985 if( tr_bencDictFindInt( t
, "dateCreated", &i
) && i
)
988 printf( " Date created: %s", ctime( &tt
) );
990 if( tr_bencDictFindBool( t
, "isPrivate", &boolVal
) )
991 printf( " Public torrent: %s\n", ( boolVal
? "No" : "Yes" ) );
992 if( tr_bencDictFindStr( t
, "comment", &str
) && str
&& *str
)
993 printf( " Comment: %s\n", str
);
994 if( tr_bencDictFindStr( t
, "creator", &str
) && str
&& *str
)
995 printf( " Creator: %s\n", str
);
996 if( tr_bencDictFindInt( t
, "pieceCount", &i
) )
997 printf( " Piece Count: %" PRId64
"\n", i
);
998 if( tr_bencDictFindInt( t
, "pieceSize", &i
) )
999 printf( " Piece Size: %s\n", strlmem( buf
, i
, sizeof( buf
) ) );
1002 printf( "LIMITS & BANDWIDTH\n" );
1003 if( tr_bencDictFindBool( t
, "downloadLimited", &boolVal
)
1004 && tr_bencDictFindInt( t
, "downloadLimit", &i
) )
1006 printf( " Download Limit: " );
1008 printf( "%s\n", tr_formatter_speed_KBps( buf
, i
, sizeof( buf
) ) );
1010 printf( "Unlimited\n" );
1012 if( tr_bencDictFindBool( t
, "uploadLimited", &boolVal
)
1013 && tr_bencDictFindInt( t
, "uploadLimit", &i
) )
1015 printf( " Upload Limit: " );
1017 printf( "%s\n", tr_formatter_speed_KBps( buf
, i
, sizeof( buf
) ) );
1019 printf( "Unlimited\n" );
1021 if( tr_bencDictFindBool( t
, "honorsSessionLimits", &boolVal
) )
1022 printf( " Honors Session Limits: %s\n", ( boolVal
? "Yes" : "No" ) );
1023 if( tr_bencDictFindInt ( t
, "peer-limit", &i
) )
1024 printf( " Peer limit: %" PRId64
"\n", i
);
1025 if (tr_bencDictFindInt (t
, "bandwidthPriority", &i
))
1026 printf (" Bandwidth Priority: %s\n",
1027 bandwidthPriorityNames
[(i
+ 1) & 3]);
1035 printFileList( tr_benc
* top
)
1037 tr_benc
*args
, *torrents
;
1039 if( ( tr_bencDictFindDict( top
, "arguments", &args
) )
1040 && ( tr_bencDictFindList( args
, "torrents", &torrents
) ) )
1043 for( i
= 0, in
= tr_bencListSize( torrents
); i
< in
; ++i
)
1045 tr_benc
* d
= tr_bencListChild( torrents
, i
);
1046 tr_benc
* files
, *priorities
, *wanteds
;
1048 if( tr_bencDictFindStr( d
, "name", &name
)
1049 && tr_bencDictFindList( d
, "files", &files
)
1050 && tr_bencDictFindList( d
, "priorities", &priorities
)
1051 && tr_bencDictFindList( d
, "wanted", &wanteds
) )
1053 int j
= 0, jn
= tr_bencListSize( files
);
1054 printf( "%s (%d files):\n", name
, jn
);
1055 printf( "%3s %4s %8s %3s %9s %s\n", "#", "Done",
1056 "Priority", "Get", "Size",
1058 for( j
= 0, jn
= tr_bencListSize( files
); j
< jn
; ++j
)
1064 const char * filename
;
1065 tr_benc
* file
= tr_bencListChild( files
, j
);
1066 if( tr_bencDictFindInt( file
, "length", &length
)
1067 && tr_bencDictFindStr( file
, "name", &filename
)
1068 && tr_bencDictFindInt( file
, "bytesCompleted", &have
)
1069 && tr_bencGetInt( tr_bencListChild( priorities
,
1071 && tr_bencGetInt( tr_bencListChild( wanteds
,
1075 double percent
= (double)have
/ length
;
1076 const char * pristr
;
1077 strlsize( sizestr
, length
, sizeof( sizestr
) );
1081 pristr
= "Low"; break;
1084 pristr
= "High"; break;
1087 pristr
= "Normal"; break;
1089 printf( "%3d: %3.0f%% %-8s %-3s %9s %s\n",
1091 floor( 100.0 * percent
),
1093 ( wanted
? "Yes" : "No" ),
1104 printPeersImpl( tr_benc
* peers
)
1107 printf( "%-20s %-12s %-5s %-6s %-6s %s\n",
1108 "Address", "Flags", "Done", "Down", "Up", "Client" );
1109 for( i
= 0, n
= tr_bencListSize( peers
); i
< n
; ++i
)
1112 const char * address
, * client
, * flagstr
;
1113 int64_t rateToClient
, rateToPeer
;
1114 tr_benc
* d
= tr_bencListChild( peers
, i
);
1116 if( tr_bencDictFindStr( d
, "address", &address
)
1117 && tr_bencDictFindStr( d
, "clientName", &client
)
1118 && tr_bencDictFindReal( d
, "progress", &progress
)
1119 && tr_bencDictFindStr( d
, "flagStr", &flagstr
)
1120 && tr_bencDictFindInt( d
, "rateToClient", &rateToClient
)
1121 && tr_bencDictFindInt( d
, "rateToPeer", &rateToPeer
) )
1123 printf( "%-20s %-12s %-5.1f %6.1f %6.1f %s\n",
1124 address
, flagstr
, (progress
*100.0),
1125 rateToClient
/ (double)tr_speed_K
,
1126 rateToPeer
/ (double)tr_speed_K
,
1133 printPeers( tr_benc
* top
)
1135 tr_benc
*args
, *torrents
;
1137 if( tr_bencDictFindDict( top
, "arguments", &args
)
1138 && tr_bencDictFindList( args
, "torrents", &torrents
) )
1141 for( i
=0, n
=tr_bencListSize( torrents
); i
<n
; ++i
)
1144 tr_benc
* torrent
= tr_bencListChild( torrents
, i
);
1145 if( tr_bencDictFindList( torrent
, "peers", &peers
) ) {
1146 printPeersImpl( peers
);
1155 printPiecesImpl( const uint8_t * raw
, size_t rawlen
, int64_t j
)
1158 char * str
= tr_base64_decode( raw
, rawlen
, &len
);
1160 for( i
=k
=0; k
<len
; ++k
) {
1162 for( e
=0; i
<j
&& e
<8; ++e
, ++i
)
1163 printf( "%c", str
[k
] & (1<<(7-e
)) ? '1' : '0' );
1173 printPieces( tr_benc
* top
)
1175 tr_benc
*args
, *torrents
;
1177 if( tr_bencDictFindDict( top
, "arguments", &args
)
1178 && tr_bencDictFindList( args
, "torrents", &torrents
) )
1181 for( i
=0, n
=tr_bencListSize( torrents
); i
<n
; ++i
)
1184 const uint8_t * raw
;
1186 tr_benc
* torrent
= tr_bencListChild( torrents
, i
);
1187 if( tr_bencDictFindRaw( torrent
, "pieces", &raw
, &rawlen
) &&
1188 tr_bencDictFindInt( torrent
, "pieceCount", &j
) ) {
1189 printPiecesImpl( raw
, rawlen
, j
);
1198 printPortTest( tr_benc
* top
)
1201 if( ( tr_bencDictFindDict( top
, "arguments", &args
) ) )
1205 if( tr_bencDictFindBool( args
, "port-is-open", &boolVal
) )
1206 printf( "Port is open: %s\n", ( boolVal
? "Yes" : "No" ) );
1211 printTorrentList( tr_benc
* top
)
1213 tr_benc
*args
, *list
;
1215 if( ( tr_bencDictFindDict( top
, "arguments", &args
) )
1216 && ( tr_bencDictFindList( args
, "torrents", &list
) ) )
1219 int64_t total_size
=0;
1220 double total_up
=0, total_down
=0;
1223 printf( "%-4s %-4s %9s %-8s %6s %6s %-5s %-11s %s\n",
1224 "ID", "Done", "Have", "ETA", "Up", "Down", "Ratio", "Status",
1227 for( i
= 0, n
= tr_bencListSize( list
); i
< n
; ++i
)
1229 int64_t id
, eta
, status
, up
, down
;
1230 int64_t sizeWhenDone
, leftUntilDone
;
1233 tr_benc
* d
= tr_bencListChild( list
, i
);
1234 if( tr_bencDictFindInt( d
, "eta", &eta
)
1235 && tr_bencDictFindInt( d
, "id", &id
)
1236 && tr_bencDictFindInt( d
, "leftUntilDone", &leftUntilDone
)
1237 && tr_bencDictFindStr( d
, "name", &name
)
1238 && tr_bencDictFindInt( d
, "rateDownload", &down
)
1239 && tr_bencDictFindInt( d
, "rateUpload", &up
)
1240 && tr_bencDictFindInt( d
, "sizeWhenDone", &sizeWhenDone
)
1241 && tr_bencDictFindInt( d
, "status", &status
)
1242 && tr_bencDictFindReal( d
, "uploadRatio", &ratio
) )
1252 tr_snprintf( doneStr
, sizeof( doneStr
), "%d%%", (int)( 100.0 * ( sizeWhenDone
- leftUntilDone
) / sizeWhenDone
) );
1254 tr_strlcpy( doneStr
, "n/a", sizeof( doneStr
) );
1256 strlsize( haveStr
, sizeWhenDone
- leftUntilDone
, sizeof( haveStr
) );
1258 if( leftUntilDone
|| eta
!= -1 )
1259 etaToString( etaStr
, sizeof( etaStr
), eta
);
1261 tr_snprintf( etaStr
, sizeof( etaStr
), "Done" );
1262 if( tr_bencDictFindInt( d
, "error", &error
) && error
)
1267 "%4d%c %4s %9s %-8s %6.1f %6.1f %5s %-11s %s\n",
1272 up
/(double)tr_speed_K
,
1273 down
/(double)tr_speed_K
,
1274 strlratio2( ratioStr
, ratio
, sizeof( ratioStr
) ),
1275 getStatusString( d
, statusStr
, sizeof( statusStr
) ),
1280 total_size
+= sizeWhenDone
- leftUntilDone
;
1284 printf( "Sum: %9s %6.1f %6.1f\n",
1285 strlsize( haveStr
, total_size
, sizeof( haveStr
) ),
1286 total_up
/(double)tr_speed_K
,
1287 total_down
/(double)tr_speed_K
);
1292 printTrackersImpl( tr_benc
* trackerStats
)
1298 for( i
=0; (( t
= tr_bencListChild( trackerStats
, i
))); ++i
)
1300 int64_t downloadCount
;
1301 tr_bool hasAnnounced
;
1306 int64_t lastAnnouncePeerCount
;
1307 const char * lastAnnounceResult
;
1308 int64_t lastAnnounceStartTime
;
1309 tr_bool lastAnnounceSucceeded
;
1310 int64_t lastAnnounceTime
;
1311 tr_bool lastAnnounceTimedOut
;
1312 const char * lastScrapeResult
;
1313 tr_bool lastScrapeSucceeded
;
1314 int64_t lastScrapeStartTime
;
1315 int64_t lastScrapeTime
;
1316 tr_bool lastScrapeTimedOut
;
1317 int64_t leecherCount
;
1318 int64_t nextAnnounceTime
;
1319 int64_t nextScrapeTime
;
1320 int64_t seederCount
;
1322 int64_t announceState
;
1323 int64_t scrapeState
;
1325 if( tr_bencDictFindInt ( t
, "downloadCount", &downloadCount
) &&
1326 tr_bencDictFindBool( t
, "hasAnnounced", &hasAnnounced
) &&
1327 tr_bencDictFindBool( t
, "hasScraped", &hasScraped
) &&
1328 tr_bencDictFindStr ( t
, "host", &host
) &&
1329 tr_bencDictFindInt ( t
, "id", &id
) &&
1330 tr_bencDictFindBool( t
, "isBackup", &isBackup
) &&
1331 tr_bencDictFindInt ( t
, "announceState", &announceState
) &&
1332 tr_bencDictFindInt ( t
, "scrapeState", &scrapeState
) &&
1333 tr_bencDictFindInt ( t
, "lastAnnouncePeerCount", &lastAnnouncePeerCount
) &&
1334 tr_bencDictFindStr ( t
, "lastAnnounceResult", &lastAnnounceResult
) &&
1335 tr_bencDictFindInt ( t
, "lastAnnounceStartTime", &lastAnnounceStartTime
) &&
1336 tr_bencDictFindBool( t
, "lastAnnounceSucceeded", &lastAnnounceSucceeded
) &&
1337 tr_bencDictFindInt ( t
, "lastAnnounceTime", &lastAnnounceTime
) &&
1338 tr_bencDictFindBool( t
, "lastAnnounceTimedOut", &lastAnnounceTimedOut
) &&
1339 tr_bencDictFindStr ( t
, "lastScrapeResult", &lastScrapeResult
) &&
1340 tr_bencDictFindInt ( t
, "lastScrapeStartTime", &lastScrapeStartTime
) &&
1341 tr_bencDictFindBool( t
, "lastScrapeSucceeded", &lastScrapeSucceeded
) &&
1342 tr_bencDictFindInt ( t
, "lastScrapeTime", &lastScrapeTime
) &&
1343 tr_bencDictFindBool( t
, "lastScrapeTimedOut", &lastScrapeTimedOut
) &&
1344 tr_bencDictFindInt ( t
, "leecherCount", &leecherCount
) &&
1345 tr_bencDictFindInt ( t
, "nextAnnounceTime", &nextAnnounceTime
) &&
1346 tr_bencDictFindInt ( t
, "nextScrapeTime", &nextScrapeTime
) &&
1347 tr_bencDictFindInt ( t
, "seederCount", &seederCount
) &&
1348 tr_bencDictFindInt ( t
, "tier", &tier
) )
1350 const time_t now
= time( NULL
);
1353 printf( " Tracker %d: %s\n", (int)(id
), host
);
1355 printf( " Backup on tier %d\n", (int)tier
);
1357 printf( " Active in tier %d\n", (int)tier
);
1361 if( hasAnnounced
&& announceState
!= TR_TRACKER_INACTIVE
)
1363 tr_strltime( buf
, now
- lastAnnounceTime
, sizeof( buf
) );
1364 if( lastAnnounceSucceeded
)
1365 printf( " Got a list of %d peers %s ago\n",
1366 (int)lastAnnouncePeerCount
, buf
);
1367 else if( lastAnnounceTimedOut
)
1368 printf( " Peer list request timed out; will retry\n" );
1370 printf( " Got an error \"%s\" %s ago\n",
1371 lastAnnounceResult
, buf
);
1374 switch( announceState
)
1376 case TR_TRACKER_INACTIVE
:
1377 printf( " No updates scheduled\n" );
1379 case TR_TRACKER_WAITING
:
1380 tr_strltime( buf
, nextAnnounceTime
- now
, sizeof( buf
) );
1381 printf( " Asking for more peers in %s\n", buf
);
1383 case TR_TRACKER_QUEUED
:
1384 printf( " Queued to ask for more peers\n" );
1386 case TR_TRACKER_ACTIVE
:
1387 tr_strltime( buf
, now
- lastAnnounceStartTime
, sizeof( buf
) );
1388 printf( " Asking for more peers now... %s\n", buf
);
1394 tr_strltime( buf
, now
- lastScrapeTime
, sizeof( buf
) );
1395 if( lastScrapeSucceeded
)
1396 printf( " Tracker had %d seeders and %d leechers %s ago\n",
1397 (int)seederCount
, (int)leecherCount
, buf
);
1398 else if( lastScrapeTimedOut
)
1399 printf( " Tracker scrape timed out; will retry\n" );
1401 printf( " Got a scrape error \"%s\" %s ago\n",
1402 lastScrapeResult
, buf
);
1405 switch( scrapeState
)
1407 case TR_TRACKER_INACTIVE
:
1409 case TR_TRACKER_WAITING
:
1410 tr_strltime( buf
, nextScrapeTime
- now
, sizeof( buf
) );
1411 printf( " Asking for peer counts in %s\n", buf
);
1413 case TR_TRACKER_QUEUED
:
1414 printf( " Queued to ask for peer counts\n" );
1416 case TR_TRACKER_ACTIVE
:
1417 tr_strltime( buf
, now
- lastScrapeStartTime
, sizeof( buf
) );
1418 printf( " Asking for peer counts now... %s\n", buf
);
1427 printTrackers( tr_benc
* top
)
1429 tr_benc
*args
, *torrents
;
1431 if( tr_bencDictFindDict( top
, "arguments", &args
)
1432 && tr_bencDictFindList( args
, "torrents", &torrents
) )
1435 for( i
=0, n
=tr_bencListSize( torrents
); i
<n
; ++i
)
1437 tr_benc
* trackerStats
;
1438 tr_benc
* torrent
= tr_bencListChild( torrents
, i
);
1439 if( tr_bencDictFindList( torrent
, "trackerStats", &trackerStats
) ) {
1440 printTrackersImpl( trackerStats
);
1449 printSession( tr_benc
* top
)
1452 if( ( tr_bencDictFindDict( top
, "arguments", &args
) ) )
1459 printf( "VERSION\n" );
1460 if( tr_bencDictFindStr( args
, "version", &str
) )
1461 printf( " Daemon version: %s\n", str
);
1462 if( tr_bencDictFindInt( args
, "rpc-version", &i
) )
1463 printf( " RPC version: %" PRId64
"\n", i
);
1464 if( tr_bencDictFindInt( args
, "rpc-version-minimum", &i
) )
1465 printf( " RPC minimum version: %" PRId64
"\n", i
);
1468 printf( "CONFIG\n" );
1469 if( tr_bencDictFindStr( args
, "config-dir", &str
) )
1470 printf( " Configuration directory: %s\n", str
);
1471 if( tr_bencDictFindStr( args
, TR_PREFS_KEY_DOWNLOAD_DIR
, &str
) )
1472 printf( " Download directory: %s\n", str
);
1473 if( tr_bencDictFindInt( args
, "download-dir-free-space", &i
) )
1474 printf( " Download directory free space: %s\n", strlsize( buf
, i
, sizeof buf
) );
1475 if( tr_bencDictFindInt( args
, TR_PREFS_KEY_PEER_PORT
, &i
) )
1476 printf( " Listenport: %" PRId64
"\n", i
);
1477 if( tr_bencDictFindBool( args
, TR_PREFS_KEY_PORT_FORWARDING
, &boolVal
) )
1478 printf( " Portforwarding enabled: %s\n", ( boolVal
? "Yes" : "No" ) );
1479 if( tr_bencDictFindBool( args
, TR_PREFS_KEY_DHT_ENABLED
, &boolVal
) )
1480 printf( " Distributed hash table enabled: %s\n", ( boolVal
? "Yes" : "No" ) );
1481 if( tr_bencDictFindBool( args
, TR_PREFS_KEY_LPD_ENABLED
, &boolVal
) )
1482 printf( " Local peer discovery enabled: %s\n", ( boolVal
? "Yes" : "No" ) );
1483 if( tr_bencDictFindBool( args
, TR_PREFS_KEY_PEX_ENABLED
, &boolVal
) )
1484 printf( " Peer exchange allowed: %s\n", ( boolVal
? "Yes" : "No" ) );
1485 if( tr_bencDictFindStr( args
, TR_PREFS_KEY_ENCRYPTION
, &str
) )
1486 printf( " Encryption: %s\n", str
);
1487 if( tr_bencDictFindInt( args
, TR_PREFS_KEY_MAX_CACHE_SIZE_MB
, &i
) )
1488 printf( " Maximum memory cache size: %s\n", tr_formatter_mem_MB( buf
, i
, sizeof( buf
) ) );
1492 tr_bool altEnabled
, altTimeEnabled
, upEnabled
, downEnabled
, seedRatioLimited
;
1493 int64_t altDown
, altUp
, altBegin
, altEnd
, altDay
, upLimit
, downLimit
, peerLimit
;
1494 double seedRatioLimit
;
1496 if( tr_bencDictFindInt ( args
, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps
, &altDown
) &&
1497 tr_bencDictFindBool( args
, TR_PREFS_KEY_ALT_SPEED_ENABLED
, &altEnabled
) &&
1498 tr_bencDictFindInt ( args
, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN
, &altBegin
) &&
1499 tr_bencDictFindBool( args
, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED
, &altTimeEnabled
) &&
1500 tr_bencDictFindInt ( args
, TR_PREFS_KEY_ALT_SPEED_TIME_END
, &altEnd
) &&
1501 tr_bencDictFindInt ( args
, TR_PREFS_KEY_ALT_SPEED_TIME_DAY
, &altDay
) &&
1502 tr_bencDictFindInt ( args
, TR_PREFS_KEY_ALT_SPEED_UP_KBps
, &altUp
) &&
1503 tr_bencDictFindInt ( args
, TR_PREFS_KEY_PEER_LIMIT_GLOBAL
, &peerLimit
) &&
1504 tr_bencDictFindInt ( args
, TR_PREFS_KEY_DSPEED_KBps
, &downLimit
) &&
1505 tr_bencDictFindBool( args
, TR_PREFS_KEY_DSPEED_ENABLED
, &downEnabled
) &&
1506 tr_bencDictFindInt ( args
, TR_PREFS_KEY_USPEED_KBps
, &upLimit
) &&
1507 tr_bencDictFindBool( args
, TR_PREFS_KEY_USPEED_ENABLED
, &upEnabled
) &&
1508 tr_bencDictFindReal( args
, "seedRatioLimit", &seedRatioLimit
) &&
1509 tr_bencDictFindBool( args
, "seedRatioLimited", &seedRatioLimited
) )
1515 printf( "LIMITS\n" );
1516 printf( " Peer limit: %" PRId64
"\n", peerLimit
);
1518 if( seedRatioLimited
)
1519 tr_snprintf( buf
, sizeof( buf
), "%.2f", seedRatioLimit
);
1521 tr_strlcpy( buf
, "Unlimited", sizeof( buf
) );
1522 printf( " Default seed ratio limit: %s\n", buf
);
1525 tr_formatter_speed_KBps( buf
, altUp
, sizeof( buf
) );
1526 else if( upEnabled
)
1527 tr_formatter_speed_KBps( buf
, upLimit
, sizeof( buf
) );
1529 tr_strlcpy( buf
, "Unlimited", sizeof( buf
) );
1530 printf( " Upload speed limit: %s (%s limit: %s; %s turtle limit: %s)\n",
1532 upEnabled
? "Enabled" : "Disabled",
1533 tr_formatter_speed_KBps( buf2
, upLimit
, sizeof( buf2
) ),
1534 altEnabled
? "Enabled" : "Disabled",
1535 tr_formatter_speed_KBps( buf3
, altUp
, sizeof( buf3
) ) );
1538 tr_formatter_speed_KBps( buf
, altDown
, sizeof( buf
) );
1539 else if( downEnabled
)
1540 tr_formatter_speed_KBps( buf
, downLimit
, sizeof( buf
) );
1542 tr_strlcpy( buf
, "Unlimited", sizeof( buf
) );
1543 printf( " Download speed limit: %s (%s limit: %s; %s turtle limit: %s)\n",
1545 downEnabled
? "Enabled" : "Disabled",
1546 tr_formatter_speed_KBps( buf2
, downLimit
, sizeof( buf2
) ),
1547 altEnabled
? "Enabled" : "Disabled",
1548 tr_formatter_speed_KBps( buf2
, altDown
, sizeof( buf2
) ) );
1550 if( altTimeEnabled
) {
1551 printf( " Turtle schedule: %02d:%02d - %02d:%02d ",
1552 (int)(altBegin
/60), (int)(altBegin
%60),
1553 (int)(altEnd
/60), (int)(altEnd
%60) );
1554 if( altDay
& TR_SCHED_SUN
) printf( "Sun " );
1555 if( altDay
& TR_SCHED_MON
) printf( "Mon " );
1556 if( altDay
& TR_SCHED_TUES
) printf( "Tue " );
1557 if( altDay
& TR_SCHED_WED
) printf( "Wed " );
1558 if( altDay
& TR_SCHED_THURS
) printf( "Thu " );
1559 if( altDay
& TR_SCHED_FRI
) printf( "Fri " );
1560 if( altDay
& TR_SCHED_SAT
) printf( "Sat " );
1568 if( tr_bencDictFindBool( args
, TR_PREFS_KEY_START
, &boolVal
) )
1569 printf( " Autostart added torrents: %s\n", ( boolVal
? "Yes" : "No" ) );
1570 if( tr_bencDictFindBool( args
, TR_PREFS_KEY_TRASH_ORIGINAL
, &boolVal
) )
1571 printf( " Delete automatically added torrents: %s\n", ( boolVal
? "Yes" : "No" ) );
1576 printSessionStats( tr_benc
* top
)
1579 if( ( tr_bencDictFindDict( top
, "arguments", &args
) ) )
1582 int64_t up
, down
, secs
, sessions
;
1584 if( tr_bencDictFindDict( args
, "current-stats", &d
)
1585 && tr_bencDictFindInt( d
, "uploadedBytes", &up
)
1586 && tr_bencDictFindInt( d
, "downloadedBytes", &down
)
1587 && tr_bencDictFindInt( d
, "secondsActive", &secs
) )
1589 printf( "\nCURRENT SESSION\n" );
1590 printf( " Uploaded: %s\n", strlsize( buf
, up
, sizeof( buf
) ) );
1591 printf( " Downloaded: %s\n", strlsize( buf
, down
, sizeof( buf
) ) );
1592 printf( " Ratio: %s\n", strlratio( buf
, up
, down
, sizeof( buf
) ) );
1593 printf( " Duration: %s\n", tr_strltime( buf
, secs
, sizeof( buf
) ) );
1596 if( tr_bencDictFindDict( args
, "cumulative-stats", &d
)
1597 && tr_bencDictFindInt( d
, "sessionCount", &sessions
)
1598 && tr_bencDictFindInt( d
, "uploadedBytes", &up
)
1599 && tr_bencDictFindInt( d
, "downloadedBytes", &down
)
1600 && tr_bencDictFindInt( d
, "secondsActive", &secs
) )
1602 printf( "\nTOTAL\n" );
1603 printf( " Started %lu times\n", (unsigned long)sessions
);
1604 printf( " Uploaded: %s\n", strlsize( buf
, up
, sizeof( buf
) ) );
1605 printf( " Downloaded: %s\n", strlsize( buf
, down
, sizeof( buf
) ) );
1606 printf( " Ratio: %s\n", strlratio( buf
, up
, down
, sizeof( buf
) ) );
1607 printf( " Duration: %s\n", tr_strltime( buf
, secs
, sizeof( buf
) ) );
1612 static char id
[4096];
1615 processResponse( const char * rpcurl
, const void * response
, size_t len
)
1618 int status
= EXIT_SUCCESS
;
1621 fprintf( stderr
, "got response (len %d):\n--------\n%*.*s\n--------\n",
1622 (int)len
, (int)len
, (int)len
, (const char*) response
);
1624 if( tr_jsonParse( NULL
, response
, len
, &top
, NULL
) )
1626 tr_nerr( MY_NAME
, "Unable to parse response \"%*.*s\"", (int)len
,
1627 (int)len
, (char*)response
);
1628 status
|= EXIT_FAILURE
;
1634 tr_bencDictFindInt( &top
, "tag", &tag
);
1639 printSession( &top
); break;
1642 printSessionStats( &top
); break;
1645 printDetails( &top
); break;
1648 printFileList( &top
); break;
1651 printTorrentList( &top
); break;
1654 printPeers( &top
); break;
1657 printPieces( &top
); break;
1660 printPortTest( &top
); break;
1663 printTrackers( &top
); break;
1665 case TAG_TORRENT_ADD
: {
1668 if( tr_bencDictFindDict( &top
, ARGUMENTS
, &b
)
1669 && tr_bencDictFindDict( b
, "torrent-added", &b
)
1670 && tr_bencDictFindInt( b
, "id", &i
) )
1671 tr_snprintf( id
, sizeof(id
), "%"PRId64
, i
);
1672 /* fall-through to default: to give success or failure msg */
1675 if( !tr_bencDictFindStr( &top
, "result", &str
) )
1676 status
|= EXIT_FAILURE
;
1678 printf( "%s responded: \"%s\"\n", rpcurl
, str
);
1679 if( strcmp( str
, "success") )
1680 status
|= EXIT_FAILURE
;
1684 tr_bencFree( &top
);
1691 tr_curl_easy_init( struct evbuffer
* writebuf
)
1693 CURL
* curl
= curl_easy_init( );
1694 curl_easy_setopt( curl
, CURLOPT_USERAGENT
, MY_NAME
"/" LONG_VERSION_STRING
);
1695 curl_easy_setopt( curl
, CURLOPT_WRITEFUNCTION
, writeFunc
);
1696 curl_easy_setopt( curl
, CURLOPT_WRITEDATA
, writebuf
);
1697 curl_easy_setopt( curl
, CURLOPT_HEADERFUNCTION
, parseResponseHeader
);
1698 curl_easy_setopt( curl
, CURLOPT_POST
, 1 );
1699 curl_easy_setopt( curl
, CURLOPT_NETRC
, CURL_NETRC_OPTIONAL
);
1700 curl_easy_setopt( curl
, CURLOPT_HTTPAUTH
, CURLAUTH_ANY
);
1701 curl_easy_setopt( curl
, CURLOPT_VERBOSE
, debug
);
1702 curl_easy_setopt( curl
, CURLOPT_ENCODING
, "" ); /* "" tells curl to fill in the blanks with what it was compiled to support */
1704 curl_easy_setopt( curl
, CURLOPT_NETRC_FILE
, netrc
);
1706 curl_easy_setopt( curl
, CURLOPT_USERPWD
, auth
);
1708 char * h
= tr_strdup_printf( "%s: %s", TR_RPC_SESSION_ID_HEADER
, sessionId
);
1709 struct curl_slist
* custom_headers
= curl_slist_append( NULL
, h
);
1710 curl_easy_setopt( curl
, CURLOPT_HTTPHEADER
, custom_headers
);
1717 flush( const char * rpcurl
, tr_benc
** benc
)
1721 int status
= EXIT_SUCCESS
;
1722 struct evbuffer
* buf
= evbuffer_new( );
1723 char * json
= tr_bencToStr( *benc
, TR_FMT_JSON_LEAN
, NULL
);
1725 curl
= tr_curl_easy_init( buf
);
1726 curl_easy_setopt( curl
, CURLOPT_URL
, rpcurl
);
1727 curl_easy_setopt( curl
, CURLOPT_POSTFIELDS
, json
);
1728 curl_easy_setopt( curl
, CURLOPT_TIMEOUT
, getTimeoutSecs( json
) );
1731 fprintf( stderr
, "posting:\n--------\n%s\n--------\n", json
);
1733 if(( res
= curl_easy_perform( curl
)))
1735 tr_nerr( MY_NAME
, "(%s) %s", rpcurl
, curl_easy_strerror( res
) );
1736 status
|= EXIT_FAILURE
;
1741 curl_easy_getinfo( curl
, CURLINFO_RESPONSE_CODE
, &response
);
1742 switch( response
) {
1744 status
|= processResponse( rpcurl
, (const char*) evbuffer_pullup( buf
, -1 ), evbuffer_get_length( buf
) );
1747 /* Session id failed. Our curl header func has already
1748 * pulled the new session id from this response's headers,
1749 * build a new CURL* and try again */
1750 curl_easy_cleanup( curl
);
1752 flush( rpcurl
, benc
);
1756 fprintf( stderr
, "Unexpected response: %s\n", evbuffer_pullup( buf
, -1 ) );
1757 status
|= EXIT_FAILURE
;
1764 evbuffer_free( buf
);
1766 curl_easy_cleanup( curl
);
1767 if( benc
!= NULL
) {
1768 tr_bencFree( *benc
);
1775 ensure_sset( tr_benc
** sset
)
1780 args
= tr_bencDictFind( *sset
, ARGUMENTS
);
1782 *sset
= tr_new0( tr_benc
, 1 );
1783 tr_bencInitDict( *sset
, 3 );
1784 tr_bencDictAddStr( *sset
, "method", "session-set" );
1785 args
= tr_bencDictAddDict( *sset
, ARGUMENTS
, 0 );
1792 ensure_tset( tr_benc
** tset
)
1797 args
= tr_bencDictFind( *tset
, ARGUMENTS
);
1799 *tset
= tr_new0( tr_benc
, 1 );
1800 tr_bencInitDict( *tset
, 3 );
1801 tr_bencDictAddStr( *tset
, "method", "torrent-set" );
1802 args
= tr_bencDictAddDict( *tset
, ARGUMENTS
, 1 );
1809 processArgs( const char * rpcurl
, int argc
, const char ** argv
)
1812 int status
= EXIT_SUCCESS
;
1813 const char * optarg
;
1820 while(( c
= tr_getopt( getUsage( ), argc
, argv
, opts
, &optarg
)))
1822 const int stepMode
= getOptMode( c
);
1824 if( !stepMode
) /* meta commands */
1828 case 'a': /* add torrent */
1829 if( sset
!= 0 ) status
|= flush( rpcurl
, &sset
);
1830 if( tadd
!= 0 ) status
|= flush( rpcurl
, &tadd
);
1831 if( tset
!= 0 ) { addIdArg( tr_bencDictFind( tset
, ARGUMENTS
), id
); status
|= flush( rpcurl
, &tset
); }
1832 tadd
= tr_new0( tr_benc
, 1 );
1833 tr_bencInitDict( tadd
, 3 );
1834 tr_bencDictAddStr( tadd
, "method", "torrent-add" );
1835 tr_bencDictAddInt( tadd
, "tag", TAG_TORRENT_ADD
);
1836 tr_bencDictAddDict( tadd
, ARGUMENTS
, 0 );
1839 case 'b': /* debug */
1843 case 'n': /* auth */
1844 auth
= tr_strdup( optarg
);
1847 case 810: /* authenv */
1849 char *authenv
= getenv("TR_AUTH");
1851 fprintf( stderr
, "The TR_AUTH environment variable is not set\n" );
1854 auth
= tr_strdup( authenv
);
1858 case 'N': /* netrc */
1859 netrc
= tr_strdup( optarg
);
1862 case 't': /* set current torrent */
1863 if( tadd
!= 0 ) status
|= flush( rpcurl
, &tadd
);
1864 if( tset
!= 0 ) { addIdArg( tr_bencDictFind( tset
, ARGUMENTS
), id
); status
|= flush( rpcurl
, &tset
); }
1865 tr_strlcpy( id
, optarg
, sizeof( id
) );
1868 case 'V': /* show version number */
1869 fprintf( stderr
, "%s %s\n", MY_NAME
, LONG_VERSION_STRING
);
1874 fprintf( stderr
, "invalid option\n" );
1876 status
|= EXIT_FAILURE
;
1881 tr_benc
* args
= tr_bencDictFind( tadd
, ARGUMENTS
);
1882 char * tmp
= getEncodedMetainfo( optarg
);
1884 tr_bencDictAddStr( args
, "metainfo", tmp
);
1886 tr_bencDictAddStr( args
, "filename", optarg
);
1889 fprintf( stderr
, "Unknown option: %s\n", optarg
);
1890 status
|= EXIT_FAILURE
;
1895 else if( stepMode
== MODE_TORRENT_GET
)
1898 tr_benc
* top
= tr_new0( tr_benc
, 1 );
1901 tr_bencInitDict( top
, 3 );
1902 tr_bencDictAddStr( top
, "method", "torrent-get" );
1903 args
= tr_bencDictAddDict( top
, ARGUMENTS
, 0 );
1904 fields
= tr_bencDictAddList( args
, "fields", 0 );
1906 if( tset
!= 0 ) { addIdArg( tr_bencDictFind( tset
, ARGUMENTS
), id
); status
|= flush( rpcurl
, &tset
); }
1910 case 'i': tr_bencDictAddInt( top
, "tag", TAG_DETAILS
);
1911 n
= TR_N_ELEMENTS( details_keys
);
1912 for( i
=0; i
<n
; ++i
) tr_bencListAddStr( fields
, details_keys
[i
] );
1913 addIdArg( args
, id
);
1915 case 'l': tr_bencDictAddInt( top
, "tag", TAG_LIST
);
1916 n
= TR_N_ELEMENTS( list_keys
);
1917 for( i
=0; i
<n
; ++i
) tr_bencListAddStr( fields
, list_keys
[i
] );
1919 case 940: tr_bencDictAddInt( top
, "tag", TAG_FILES
);
1920 n
= TR_N_ELEMENTS( files_keys
);
1921 for( i
=0; i
<n
; ++i
) tr_bencListAddStr( fields
, files_keys
[i
] );
1922 addIdArg( args
, id
);
1924 case 941: tr_bencDictAddInt( top
, "tag", TAG_PEERS
);
1925 tr_bencListAddStr( fields
, "peers" );
1926 addIdArg( args
, id
);
1928 case 942: tr_bencDictAddInt( top
, "tag", TAG_PIECES
);
1929 tr_bencListAddStr( fields
, "pieces" );
1930 tr_bencListAddStr( fields
, "pieceCount" );
1931 addIdArg( args
, id
);
1933 case 943: tr_bencDictAddInt( top
, "tag", TAG_TRACKERS
);
1934 tr_bencListAddStr( fields
, "trackerStats" );
1935 addIdArg( args
, id
);
1937 default: assert( "unhandled value" && 0 );
1940 status
|= flush( rpcurl
, &top
);
1942 else if( stepMode
== MODE_SESSION_SET
)
1944 tr_benc
* args
= ensure_sset( &sset
);
1948 case 800: tr_bencDictAddStr( args
, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME
, optarg
);
1949 tr_bencDictAddBool( args
, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED
, TRUE
);
1951 case 801: tr_bencDictAddBool( args
, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED
, FALSE
);
1953 case 970: tr_bencDictAddBool( args
, TR_PREFS_KEY_ALT_SPEED_ENABLED
, TRUE
);
1955 case 971: tr_bencDictAddBool( args
, TR_PREFS_KEY_ALT_SPEED_ENABLED
, FALSE
);
1957 case 972: tr_bencDictAddInt( args
, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps
, numarg( optarg
) );
1959 case 973: tr_bencDictAddInt( args
, TR_PREFS_KEY_ALT_SPEED_UP_KBps
, numarg( optarg
) );
1961 case 974: tr_bencDictAddBool( args
, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED
, TRUE
);
1963 case 975: tr_bencDictAddBool( args
, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED
, FALSE
);
1965 case 976: addTime( args
, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN
, optarg
);
1967 case 977: addTime( args
, TR_PREFS_KEY_ALT_SPEED_TIME_END
, optarg
);
1969 case 978: addDays( args
, TR_PREFS_KEY_ALT_SPEED_TIME_DAY
, optarg
);
1971 case 'c': tr_bencDictAddStr( args
, TR_PREFS_KEY_INCOMPLETE_DIR
, optarg
);
1972 tr_bencDictAddBool( args
, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED
, TRUE
);
1974 case 'C': tr_bencDictAddBool( args
, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED
, FALSE
);
1976 case 'e': tr_bencDictAddInt( args
, TR_PREFS_KEY_MAX_CACHE_SIZE_MB
, atoi(optarg
) );
1978 case 910: tr_bencDictAddStr( args
, TR_PREFS_KEY_ENCRYPTION
, "required" );
1980 case 911: tr_bencDictAddStr( args
, TR_PREFS_KEY_ENCRYPTION
, "preferred" );
1982 case 912: tr_bencDictAddStr( args
, TR_PREFS_KEY_ENCRYPTION
, "tolerated" );
1984 case 'm': tr_bencDictAddBool( args
, TR_PREFS_KEY_PORT_FORWARDING
, TRUE
);
1986 case 'M': tr_bencDictAddBool( args
, TR_PREFS_KEY_PORT_FORWARDING
, FALSE
);
1988 case 'o': tr_bencDictAddBool( args
, TR_PREFS_KEY_DHT_ENABLED
, TRUE
);
1990 case 'O': tr_bencDictAddBool( args
, TR_PREFS_KEY_DHT_ENABLED
, FALSE
);
1992 case 'p': tr_bencDictAddInt( args
, TR_PREFS_KEY_PEER_PORT
, numarg( optarg
) );
1994 case 'P': tr_bencDictAddBool( args
, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START
, TRUE
);
1996 case 'x': tr_bencDictAddBool( args
, TR_PREFS_KEY_PEX_ENABLED
, TRUE
);
1998 case 'X': tr_bencDictAddBool( args
, TR_PREFS_KEY_PEX_ENABLED
, FALSE
);
2000 case 'y': tr_bencDictAddBool( args
, TR_PREFS_KEY_LPD_ENABLED
, TRUE
);
2002 case 'Y': tr_bencDictAddBool( args
, TR_PREFS_KEY_LPD_ENABLED
, FALSE
);
2004 case 953: tr_bencDictAddReal( args
, "seedRatioLimit", atof(optarg
) );
2005 tr_bencDictAddBool( args
, "seedRatioLimited", TRUE
);
2007 case 954: tr_bencDictAddBool( args
, "seedRatioLimited", FALSE
);
2009 case 990: tr_bencDictAddBool( args
, TR_PREFS_KEY_START
, FALSE
);
2011 case 991: tr_bencDictAddBool( args
, TR_PREFS_KEY_START
, TRUE
);
2013 case 992: tr_bencDictAddBool( args
, TR_PREFS_KEY_TRASH_ORIGINAL
, TRUE
);
2015 case 993: tr_bencDictAddBool( args
, TR_PREFS_KEY_TRASH_ORIGINAL
, FALSE
);
2017 default: assert( "unhandled value" && 0 );
2021 else if( stepMode
== ( MODE_SESSION_SET
| MODE_TORRENT_SET
) )
2023 tr_benc
* targs
= 0;
2024 tr_benc
* sargs
= 0;
2027 targs
= ensure_tset( &tset
);
2029 sargs
= ensure_sset( &sset
);
2033 case 'd': if( targs
) {
2034 tr_bencDictAddInt( targs
, "downloadLimit", numarg( optarg
) );
2035 tr_bencDictAddBool( targs
, "downloadLimited", TRUE
);
2037 tr_bencDictAddInt( sargs
, TR_PREFS_KEY_DSPEED_KBps
, numarg( optarg
) );
2038 tr_bencDictAddBool( sargs
, TR_PREFS_KEY_DSPEED_ENABLED
, TRUE
);
2041 case 'D': if( targs
)
2042 tr_bencDictAddBool( targs
, "downloadLimited", FALSE
);
2044 tr_bencDictAddBool( sargs
, TR_PREFS_KEY_DSPEED_ENABLED
, FALSE
);
2046 case 'u': if( targs
) {
2047 tr_bencDictAddInt( targs
, "uploadLimit", numarg( optarg
) );
2048 tr_bencDictAddBool( targs
, "uploadLimited", TRUE
);
2050 tr_bencDictAddInt( sargs
, TR_PREFS_KEY_USPEED_KBps
, numarg( optarg
) );
2051 tr_bencDictAddBool( sargs
, TR_PREFS_KEY_USPEED_ENABLED
, TRUE
);
2054 case 'U': if( targs
)
2055 tr_bencDictAddBool( targs
, "uploadLimited", FALSE
);
2057 tr_bencDictAddBool( sargs
, TR_PREFS_KEY_USPEED_ENABLED
, FALSE
);
2059 case 930: if( targs
)
2060 tr_bencDictAddInt( targs
, "peer-limit", atoi(optarg
) );
2062 tr_bencDictAddInt( sargs
, TR_PREFS_KEY_PEER_LIMIT_GLOBAL
, atoi(optarg
) );
2064 default: assert( "unhandled value" && 0 );
2068 else if( stepMode
== MODE_TORRENT_SET
)
2070 tr_benc
* args
= ensure_tset( &tset
);
2074 case 712: tr_bencListAddInt( tr_bencDictAddList( args
, "trackerRemove", 1 ), atoi( optarg
) );
2076 case 950: tr_bencDictAddReal( args
, "seedRatioLimit", atof(optarg
) );
2077 tr_bencDictAddInt( args
, "seedRatioMode", TR_RATIOLIMIT_SINGLE
);
2079 case 951: tr_bencDictAddInt( args
, "seedRatioMode", TR_RATIOLIMIT_GLOBAL
);
2081 case 952: tr_bencDictAddInt( args
, "seedRatioMode", TR_RATIOLIMIT_UNLIMITED
);
2083 case 984: tr_bencDictAddBool( args
, "honorsSessionLimits", TRUE
);
2085 case 985: tr_bencDictAddBool( args
, "honorsSessionLimits", FALSE
);
2087 default: assert( "unhandled value" && 0 );
2091 else if( stepMode
== ( MODE_TORRENT_SET
| MODE_TORRENT_ADD
) )
2096 args
= tr_bencDictFind( tadd
, ARGUMENTS
);
2098 args
= ensure_tset( &tset
);
2102 case 'g': addFiles( args
, "files-wanted", optarg
);
2104 case 'G': addFiles( args
, "files-unwanted", optarg
);
2106 case 900: addFiles( args
, "priority-high", optarg
);
2108 case 901: addFiles( args
, "priority-normal", optarg
);
2110 case 902: addFiles( args
, "priority-low", optarg
);
2112 case 700: tr_bencDictAddInt( args
, "bandwidthPriority", 1 );
2114 case 701: tr_bencDictAddInt( args
, "bandwidthPriority", 0 );
2116 case 702: tr_bencDictAddInt( args
, "bandwidthPriority", -1 );
2118 case 710: tr_bencListAddStr( tr_bencDictAddList( args
, "trackerAdd", 1 ), optarg
);
2120 default: assert( "unhandled value" && 0 );
2124 else if( c
== 961 ) /* set location */
2128 tr_benc
* args
= tr_bencDictFind( tadd
, ARGUMENTS
);
2129 tr_bencDictAddStr( args
, "download-dir", optarg
);
2134 tr_benc
* top
= tr_new0( tr_benc
, 1 );
2135 tr_bencInitDict( top
, 2 );
2136 tr_bencDictAddStr( top
, "method", "torrent-set-location" );
2137 args
= tr_bencDictAddDict( top
, ARGUMENTS
, 3 );
2138 tr_bencDictAddStr( args
, "location", optarg
);
2139 tr_bencDictAddBool( args
, "move", FALSE
);
2140 addIdArg( args
, id
);
2141 status
|= flush( rpcurl
, &top
);
2147 case 920: /* session-info */
2149 tr_benc
* top
= tr_new0( tr_benc
, 1 );
2150 tr_bencInitDict( top
, 2 );
2151 tr_bencDictAddStr( top
, "method", "session-get" );
2152 tr_bencDictAddInt( top
, "tag", TAG_SESSION
);
2153 status
|= flush( rpcurl
, &top
);
2156 case 's': /* start */
2159 tr_bencDictAddBool( tr_bencDictFind( tadd
, "arguments" ), "paused", FALSE
);
2161 tr_benc
* top
= tr_new0( tr_benc
, 1 );
2162 tr_bencInitDict( top
, 2 );
2163 tr_bencDictAddStr( top
, "method", "torrent-start" );
2164 addIdArg( tr_bencDictAddDict( top
, ARGUMENTS
, 1 ), id
);
2165 status
|= flush( rpcurl
, &top
);
2169 case 'S': /* stop */
2172 tr_bencDictAddBool( tr_bencDictFind( tadd
, "arguments" ), "paused", TRUE
);
2174 tr_benc
* top
= tr_new0( tr_benc
, 1 );
2175 tr_bencInitDict( top
, 2 );
2176 tr_bencDictAddStr( top
, "method", "torrent-stop" );
2177 addIdArg( tr_bencDictAddDict( top
, ARGUMENTS
, 1 ), id
);
2178 status
|= flush( rpcurl
, &top
);
2184 char * path
= absolutify( optarg
);
2186 tr_bencDictAddStr( tr_bencDictFind( tadd
, "arguments" ), "download-dir", path
);
2188 tr_benc
* args
= ensure_sset( &sset
);
2189 tr_bencDictAddStr( args
, "download-dir", path
);
2196 tr_benc
* top
= tr_new0( tr_benc
, 1 );
2197 tr_bencInitDict( top
, 1 );
2198 tr_bencDictAddStr( top
, "method", "session-close" );
2199 status
|= flush( rpcurl
, &top
);
2204 tr_benc
* top
= tr_new0( tr_benc
, 1 );
2205 tr_bencInitDict( top
, 1 );
2206 tr_bencDictAddStr( top
, "method", "blocklist-update" );
2207 status
|= flush( rpcurl
, &top
);
2212 tr_benc
* top
= tr_new0( tr_benc
, 1 );
2213 tr_bencInitDict( top
, 2 );
2214 tr_bencDictAddStr( top
, "method", "session-stats" );
2215 tr_bencDictAddInt( top
, "tag", TAG_STATS
);
2216 status
|= flush( rpcurl
, &top
);
2221 tr_benc
* top
= tr_new0( tr_benc
, 1 );
2222 tr_bencInitDict( top
, 2 );
2223 tr_bencDictAddStr( top
, "method", "port-test" );
2224 tr_bencDictAddInt( top
, "tag", TAG_PORTTEST
);
2225 status
|= flush( rpcurl
, &top
);
2231 if( tset
!= 0 ) { addIdArg( tr_bencDictFind( tset
, ARGUMENTS
), id
); status
|= flush( rpcurl
, &tset
); }
2232 top
= tr_new0( tr_benc
, 1 );
2233 tr_bencInitDict( top
, 2 );
2234 tr_bencDictAddStr( top
, "method", "torrent-reannounce" );
2235 addIdArg( tr_bencDictAddDict( top
, ARGUMENTS
, 1 ), id
);
2236 status
|= flush( rpcurl
, &top
);
2242 if( tset
!= 0 ) { addIdArg( tr_bencDictFind( tset
, ARGUMENTS
), id
); status
|= flush( rpcurl
, &tset
); }
2243 top
= tr_new0( tr_benc
, 1 );
2244 tr_bencInitDict( top
, 2 );
2245 tr_bencDictAddStr( top
, "method", "torrent-verify" );
2246 addIdArg( tr_bencDictAddDict( top
, ARGUMENTS
, 1 ), id
);
2247 status
|= flush( rpcurl
, &top
);
2254 tr_benc
* top
= tr_new0( tr_benc
, 1 );
2255 tr_bencInitDict( top
, 2 );
2256 tr_bencDictAddStr( top
, "method", "torrent-remove" );
2257 args
= tr_bencDictAddDict( top
, ARGUMENTS
, 2 );
2258 tr_bencDictAddBool( args
, "delete-local-data", c
=='R' );
2259 addIdArg( args
, id
);
2260 status
|= flush( rpcurl
, &top
);
2266 tr_benc
* top
= tr_new0( tr_benc
, 1 );
2267 tr_bencInitDict( top
, 2 );
2268 tr_bencDictAddStr( top
, "method", "torrent-set-location" );
2269 args
= tr_bencDictAddDict( top
, ARGUMENTS
, 3 );
2270 tr_bencDictAddStr( args
, "location", optarg
);
2271 tr_bencDictAddBool( args
, "move", TRUE
);
2272 addIdArg( args
, id
);
2273 status
|= flush( rpcurl
, &top
);
2278 fprintf( stderr
, "got opt [%d]\n", c
);
2285 if( tadd
!= 0 ) status
|= flush( rpcurl
, &tadd
);
2286 if( tset
!= 0 ) { addIdArg( tr_bencDictFind( tset
, ARGUMENTS
), id
); status
|= flush( rpcurl
, &tset
); }
2287 if( sset
!= 0 ) status
|= flush( rpcurl
, &sset
);
2291 /* [host:port] or [host] or [port] or [http://host:port/transmission/] */
2293 getHostAndPortAndRpcUrl( int * argc
, char ** argv
,
2294 char ** host
, int * port
, char ** rpcurl
)
2296 if( *argv
[1] != '-' )
2299 const char * s
= argv
[1];
2300 const char * delim
= strchr( s
, ':' );
2301 if( !strncmp(s
, "http://", 7 ) ) /* user passed in full rpc url */
2303 *rpcurl
= tr_strdup_printf( "%s/rpc/", s
);
2305 else if( delim
) /* user passed in both host and port */
2307 *host
= tr_strndup( s
, delim
- s
);
2308 *port
= atoi( delim
+ 1 );
2313 const int i
= strtol( s
, &end
, 10 );
2314 if( !*end
) /* user passed in a port */
2316 else /* user passed in a host */
2317 *host
= tr_strdup( s
);
2321 for( i
= 1; i
< *argc
; ++i
)
2322 argv
[i
] = argv
[i
+ 1];
2327 main( int argc
, char ** argv
)
2329 int port
= DEFAULT_PORT
;
2331 char * rpcurl
= NULL
;
2332 int exit_status
= EXIT_SUCCESS
;
2336 return EXIT_FAILURE
;
2339 tr_formatter_mem_init( MEM_K
, MEM_K_STR
, MEM_M_STR
, MEM_G_STR
, MEM_T_STR
);
2340 tr_formatter_size_init( DISK_K
,DISK_K_STR
, DISK_M_STR
, DISK_G_STR
, DISK_T_STR
);
2341 tr_formatter_speed_init( SPEED_K
, SPEED_K_STR
, SPEED_M_STR
, SPEED_G_STR
, SPEED_T_STR
);
2343 getHostAndPortAndRpcUrl( &argc
, argv
, &host
, &port
, &rpcurl
);
2345 host
= tr_strdup( DEFAULT_HOST
);
2346 if( rpcurl
== NULL
)
2347 rpcurl
= tr_strdup_printf( "http://%s:%d%s", host
, port
, DEFAULT_URL
);
2349 exit_status
= processArgs( rpcurl
, argc
, (const char**)argv
);