2
* This file Copyright (C) 2008-2010 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 11318 2010-10-16 16:08:40Z charles $
14
#include <ctype.h> /* isspace */
19
#include <string.h> /* strcmp */
22
#include <direct.h> /* getcwd */
24
#include <unistd.h> /* getcwd */
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)
44
#define ARGUMENTS "arguments"
48
#define MEM_K_STR "KiB"
49
#define MEM_M_STR "MiB"
50
#define MEM_G_STR "GiB"
51
#define MEM_T_STR "TiB"
54
#define DISK_B_STR "B"
55
#define DISK_K_STR "KiB"
56
#define DISK_M_STR "MiB"
57
#define DISK_G_STR "GiB"
58
#define DISK_T_STR "TiB"
61
#define SPEED_B_STR "B/s"
62
#define SPEED_K_STR "KiB/s"
63
#define SPEED_M_STR "MiB/s"
64
#define SPEED_G_STR "GiB/s"
65
#define SPEED_T_STR "TiB/s"
69
**** Display Utilities
74
etaToString( char * buf, size_t buflen, int64_t eta )
77
tr_snprintf( buf, buflen, "Unknown" );
79
tr_snprintf( buf, buflen, "%" PRId64 "sec", eta );
80
else if( eta < ( 60 * 60 ) )
81
tr_snprintf( buf, buflen, "%" PRId64 " min", eta / 60 );
82
else if( eta < ( 60 * 60 * 24 ) )
83
tr_snprintf( buf, buflen, "%" PRId64 " hrs", eta / ( 60 * 60 ) );
85
tr_snprintf( buf, buflen, "%" PRId64 " days", eta / ( 60 * 60 * 24 ) );
89
tr_strltime( char * buf, int seconds, size_t buflen )
91
int days, hours, minutes;
92
char d[128], h[128], m[128], s[128];
97
days = seconds / 86400;
98
hours = ( seconds % 86400 ) / 3600;
99
minutes = ( seconds % 3600 ) / 60;
100
seconds = ( seconds % 3600 ) % 60;
102
tr_snprintf( d, sizeof( d ), "%'d day%s", days, days==1?"":"s" );
103
tr_snprintf( h, sizeof( h ), "%'d hour%s", hours, hours==1?"":"s" );
104
tr_snprintf( m, sizeof( m ), "%'d minute%s", minutes, minutes==1?"":"s" );
105
tr_snprintf( s, sizeof( s ), "%'d second%s", seconds, seconds==1?"":"s" );
109
if( days >= 4 || !hours )
110
tr_strlcpy( buf, d, buflen );
112
tr_snprintf( buf, buflen, "%s, %s", d, h );
116
if( hours >= 4 || !minutes )
117
tr_strlcpy( buf, h, buflen );
119
tr_snprintf( buf, buflen, "%s, %s", h, m );
123
if( minutes >= 4 || !seconds )
124
tr_strlcpy( buf, m, buflen );
126
tr_snprintf( buf, buflen, "%s, %s", m, s );
128
else tr_strlcpy( buf, s, buflen );
134
strlpercent( char * buf, double x, size_t buflen )
136
return tr_strpercent( buf, x, buflen );
140
strlratio2( char * buf, double ratio, size_t buflen )
142
return tr_strratio( buf, buflen, ratio, "Inf" );
146
strlratio( char * buf, int64_t numerator, int64_t denominator, size_t buflen )
150
if( denominator != 0 )
151
ratio = numerator / (double)denominator;
152
else if( numerator != 0 )
153
ratio = TR_RATIO_INF;
157
return strlratio2( buf, ratio, buflen );
161
strlmem( char * buf, int64_t bytes, size_t buflen )
164
tr_strlcpy( buf, "None", buflen );
166
tr_formatter_mem_B( buf, bytes, buflen );
172
strlsize( char * buf, int64_t bytes, size_t buflen )
175
tr_strlcpy( buf, "None", buflen );
177
tr_formatter_size_B( buf, bytes, buflen );
200
MY_NAME" "LONG_VERSION_STRING"\n"
201
"A fast and easy BitTorrent client\n"
202
"http://www.transmissionbt.com/\n"
205
" [host] [options]\n"
207
MY_NAME " [port] [options]\n"
209
MY_NAME " [host:port] [options]\n"
211
"See the man page for detailed explanations and many examples.";
216
**** Command-Line Arguments
220
static tr_option opts[] =
222
{ 'a', "add", "Add torrent files by filename or URL", "a", 0, NULL },
223
{ 970, "alt-speed", "Use the alternate Limits", "as", 0, NULL },
224
{ 971, "no-alt-speed", "Don't use the alternate Limits", "AS", 0, NULL },
225
{ 972, "alt-speed-downlimit", "max alternate download speed (in "SPEED_K_STR")", "asd", 1, "<speed>" },
226
{ 973, "alt-speed-uplimit", "max alternate upload speed (in "SPEED_K_STR")", "asu", 1, "<speed>" },
227
{ 974, "alt-speed-scheduler", "Use the scheduled on/off times", "asc", 0, NULL },
228
{ 975, "no-alt-speed-scheduler", "Don't use the scheduled on/off times", "ASC", 0, NULL },
229
{ 976, "alt-speed-time-begin", "Time to start using the alt speed limits (in hhmm)", NULL, 1, "<time>" },
230
{ 977, "alt-speed-time-end", "Time to stop using the alt speed limits (in hhmm)", NULL, 1, "<time>" },
231
{ 978, "alt-speed-days", "Numbers for any/all days of the week - eg. \"1-7\"", NULL, 1, "<days>" },
232
{ 963, "blocklist-update", "Blocklist update", NULL, 0, NULL },
233
{ 'c', "incomplete-dir", "Where to store new torrents until they're complete", "c", 1, "<dir>" },
234
{ 'C', "no-incomplete-dir", "Don't store incomplete torrents in a different location", "C", 0, NULL },
235
{ 'b', "debug", "Print debugging information", "b", 0, NULL },
236
{ 'd', "downlimit", "Set the max download speed in "SPEED_K_STR" for the current torrent(s) or globally", "d", 1, "<speed>" },
237
{ 'D', "no-downlimit", "Disable max download speed for the current torrent(s) or globally", "D", 0, NULL },
238
{ 'e', "cache", "Set the maximum size of the session's memory cache (in " MEM_M_STR ")", "e", 1, "<size>" },
239
{ 910, "encryption-required", "Encrypt all peer connections", "er", 0, NULL },
240
{ 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", 0, NULL },
241
{ 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", 0, NULL },
242
{ 940, "files", "List the current torrent(s)' files", "f", 0, NULL },
243
{ 'g', "get", "Mark files for download", "g", 1, "<files>" },
244
{ 'G', "no-get", "Mark files for not downloading", "G", 1, "<files>" },
245
{ 'i', "info", "Show the current torrent(s)' details", "i", 0, NULL },
246
{ 940, "info-files", "List the current torrent(s)' files", "if", 0, NULL },
247
{ 941, "info-peers", "List the current torrent(s)' peers", "ip", 0, NULL },
248
{ 942, "info-pieces", "List the current torrent(s)' pieces", "ic", 0, NULL },
249
{ 943, "info-trackers", "List the current torrent(s)' trackers", "it", 0, NULL },
250
{ 920, "session-info", "Show the session's details", "si", 0, NULL },
251
{ 921, "session-stats", "Show the session's statistics", "st", 0, NULL },
252
{ 'l', "list", "List all torrents", "l", 0, NULL },
253
{ 960, "move", "Move current torrent's data to a new folder", NULL, 1, "<path>" },
254
{ 961, "find", "Tell Transmission where to find a torrent's data", NULL, 1, "<path>" },
255
{ 'm', "portmap", "Enable portmapping via NAT-PMP or UPnP", "m", 0, NULL },
256
{ 'M', "no-portmap", "Disable portmapping", "M", 0, NULL },
257
{ 'n', "auth", "Set username and password", "n", 1, "<user:pw>" },
258
{ 'N', "netrc", "Set authentication info from a .netrc file", "N", 1, "<file>" },
259
{ 'o', "dht", "Enable distributed hash tables (DHT)", "o", 0, NULL },
260
{ 'O', "no-dht", "Disable distributed hash tables (DHT)", "O", 0, NULL },
261
{ 'p', "port", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "p", 1, "<port>" },
262
{ 962, "port-test", "Port testing", "pt", 0, NULL },
263
{ 'P', "random-port", "Random port for incomping peers", "P", 0, NULL },
264
{ 900, "priority-high", "Try to download these file(s) first", "ph", 1, "<files>" },
265
{ 901, "priority-normal", "Try to download these file(s) normally", "pn", 1, "<files>" },
266
{ 902, "priority-low", "Try to download these file(s) last", "pl", 1, "<files>" },
267
{ 700, "bandwidth-high", "Give this torrent first chance at available bandwidth", "Bh", 0, NULL },
268
{ 701, "bandwidth-normal", "Give this torrent bandwidth left over by high priority torrents", "Bn", 0, NULL },
269
{ 702, "bandwidth-low", "Give this torrent bandwidth left over by high and normal priority torrents", "Bl", 0, NULL },
270
{ 'r', "remove", "Remove the current torrent(s)", "r", 0, NULL },
271
{ 930, "peers", "Set the maximum number of peers for the current torrent(s) or globally", "pr", 1, "<max>" },
272
{ 'R', "remove-and-delete", "Remove the current torrent(s) and delete local data", NULL, 0, NULL },
273
{ 800, "torrent-done-script", "Specify a script to run when a torrent finishes", NULL, 1, "<file>" },
274
{ 801, "no-torrent-done-script", "Don't run a script when torrents finish", NULL, 0, NULL },
275
{ 950, "seedratio", "Let the current torrent(s) seed until a specific ratio", "sr", 1, "ratio" },
276
{ 951, "seedratio-default", "Let the current torrent(s) use the global seedratio settings", "srd", 0, NULL },
277
{ 952, "no-seedratio", "Let the current torrent(s) seed regardless of ratio", "SR", 0, NULL },
278
{ 953, "global-seedratio", "All torrents, unless overridden by a per-torrent setting, should seed until a specific ratio", "gsr", 1, "ratio" },
279
{ 954, "no-global-seedratio", "All torrents, unless overridden by a per-torrent setting, should seed regardless of ratio", "GSR", 0, NULL },
280
{ 710, "tracker-add", "Add a tracker to a torrent", "ta", 1, "<tracker>" },
281
{ 712, "tracker-remove", "Remove a tracker from a torrent", "tr", 1, "<trackerId>" },
282
{ 's', "start", "Start the current torrent(s)", "s", 0, NULL },
283
{ 'S', "stop", "Stop the current torrent(s)", "S", 0, NULL },
284
{ 't', "torrent", "Set the current torrent(s)", "t", 1, "<torrent>" },
285
{ 990, "start-paused", "Start added torrents paused", NULL, 0, NULL },
286
{ 991, "no-start-paused", "Start added torrents unpaused", NULL, 0, NULL },
287
{ 992, "trash-torrent", "Delete torrents after adding", NULL, 0, NULL },
288
{ 993, "no-trash-torrent", "Do not delete torrents after adding", NULL, 0, NULL },
289
{ 984, "honor-session", "Make the current torrent(s) honor the session limits", "hl", 0, NULL },
290
{ 985, "no-honor-session", "Make the current torrent(s) not honor the session limits", "HL", 0, NULL },
291
{ 'u', "uplimit", "Set the max upload speed in "SPEED_K_STR" for the current torrent(s) or globally", "u", 1, "<speed>" },
292
{ 'U', "no-uplimit", "Disable max upload speed for the current torrent(s) or globally", "U", 0, NULL },
293
{ 'v', "verify", "Verify the current torrent(s)", "v", 0, NULL },
294
{ 'V', "version", "Show version number and exit", "V", 0, NULL },
295
{ 'w', "download-dir", "When adding a new torrent, set its download folder. Otherwise, set the default download folder", "w", 1, "<path>" },
296
{ 'x', "pex", "Enable peer exchange (PEX)", "x", 0, NULL },
297
{ 'X', "no-pex", "Disable peer exchange (PEX)", "X", 0, NULL },
298
{ 'y', "lpd", "Enable local peer discovery (LPD)", "y", 0, NULL },
299
{ 'Y', "no-lpd", "Disable local peer discovery (LPD)", "Y", 0, NULL },
300
{ 941, "peer-info", "List the current torrent(s)' peers", "pi", 0, NULL },
301
{ 0, NULL, NULL, NULL, 0, NULL }
307
tr_getopt_usage( MY_NAME, getUsage( ), opts );
311
numarg( const char * arg )
314
const long num = strtol( arg, &end, 10 );
318
fprintf( stderr, "Not a number: \"%s\"\n", arg );
320
exit( EXIT_FAILURE );
327
MODE_TORRENT_START = (1<<0),
328
MODE_TORRENT_STOP = (1<<1),
329
MODE_TORRENT_VERIFY = (1<<2),
330
MODE_TORRENT_REANNOUNCE = (1<<3),
331
MODE_TORRENT_SET = (1<<4),
332
MODE_TORRENT_GET = (1<<5),
333
MODE_TORRENT_ADD = (1<<6),
334
MODE_TORRENT_REMOVE = (1<<7),
335
MODE_TORRENT_SET_LOCATION = (1<<8),
336
MODE_SESSION_SET = (1<<9),
337
MODE_SESSION_GET = (1<<10),
338
MODE_SESSION_STATS = (1<<11),
339
MODE_BLOCKLIST_UPDATE = (1<<12),
340
MODE_PORT_TEST = (1<<13)
344
getOptMode( int val )
350
case 'a': /* add torrent */
351
case 'b': /* debug */
353
case 'N': /* netrc */
354
case 't': /* set current torrent */
355
case 'V': /* show version number */
358
case 'c': /* incomplete-dir */
359
case 'C': /* no-incomplete-dir */
360
case 'e': /* cache */
361
case 'm': /* portmap */
362
case 'M': /* "no-portmap */
364
case 'O': /* no-dht */
365
case 'p': /* incoming peer port */
366
case 'P': /* random incoming peer port */
368
case 'X': /* no-pex */
370
case 'Y': /* no-lpd */
371
case 800: /* torrent-done-script */
372
case 801: /* no-torrent-done-script */
373
case 970: /* alt-speed */
374
case 971: /* no-alt-speed */
375
case 972: /* alt-speed-downlimit */
376
case 973: /* alt-speed-uplimit */
377
case 974: /* alt-speed-scheduler */
378
case 975: /* no-alt-speed-scheduler */
379
case 976: /* alt-speed-time-begin */
380
case 977: /* alt-speed-time-end */
381
case 978: /* alt-speed-days */
382
case 910: /* encryption-required */
383
case 911: /* encryption-preferred */
384
case 912: /* encryption-tolerated */
385
case 953: /* global-seedratio */
386
case 954: /* no-global-seedratio */
387
case 990: /* start-paused */
388
case 991: /* no-start-paused */
389
case 992: /* trash-torrent */
390
case 993: /* no-trash-torrent */
391
return MODE_SESSION_SET;
393
case 712: /* tracker-remove */
394
case 950: /* seedratio */
395
case 951: /* seedratio-default */
396
case 952: /* no-seedratio */
397
case 984: /* honor-session */
398
case 985: /* no-honor-session */
399
return MODE_TORRENT_SET;
401
case 920: /* session-info */
402
return MODE_SESSION_GET;
405
case 'G': /* no-get */
406
case 700: /* torrent priority-high */
407
case 701: /* torrent priority-normal */
408
case 702: /* torrent priority-low */
409
case 710: /* tracker-add */
410
case 900: /* file priority-high */
411
case 901: /* file priority-normal */
412
case 902: /* file priority-low */
413
return MODE_TORRENT_SET | MODE_TORRENT_ADD;
416
return MODE_TORRENT_SET_LOCATION | MODE_TORRENT_ADD;
419
case 'l': /* list all torrents */
420
case 940: /* info-files */
421
case 941: /* info-peer */
422
case 942: /* info-pieces */
423
case 943: /* info-tracker */
424
return MODE_TORRENT_GET;
426
case 'd': /* download speed limit */
427
case 'D': /* no download speed limit */
428
case 'u': /* upload speed limit */
429
case 'U': /* no upload speed limit */
430
case 930: /* peers */
431
return MODE_SESSION_SET | MODE_TORRENT_SET;
433
case 's': /* start */
434
return MODE_TORRENT_START | MODE_TORRENT_ADD;
437
return MODE_TORRENT_STOP | MODE_TORRENT_ADD;
439
case 'w': /* download-dir */
440
return MODE_SESSION_SET | MODE_TORRENT_ADD;
442
case 963: /* blocklist-update */
443
return MODE_BLOCKLIST_UPDATE;
445
case 921: /* session-stats */
446
return MODE_SESSION_STATS;
448
case 'v': /* verify */
449
return MODE_TORRENT_VERIFY;
451
case 962: /* port-test */
452
return MODE_PORT_TEST;
454
case 'r': /* remove */
455
case 'R': /* remove and delete */
456
return MODE_TORRENT_REMOVE;
459
return MODE_TORRENT_SET_LOCATION;
462
fprintf( stderr, "unrecognized argument %d\n", val );
463
assert( "unrecognized argument" && 0 );
468
static tr_bool debug = 0;
469
static char * auth = NULL;
470
static char * netrc = NULL;
471
static char * sessionId = NULL;
479
_getcwd( buf, sizeof( buf ) );
481
getcwd( buf, sizeof( buf ) );
483
return tr_strdup( buf );
487
absolutify( const char * path )
492
buf = tr_strdup( path );
494
char * cwd = tr_getcwd( );
495
buf = tr_buildPath( cwd, path, NULL );
503
getEncodedMetainfo( const char * filename )
507
uint8_t * buf = tr_loadFile( filename, &len );
511
b64 = tr_base64_encode( buf, len, NULL );
518
addIdArg( tr_benc * args, const char * id )
524
"No torrent specified! Please use the -t option first.\n" );
525
id = "-1"; /* no torrent will have this ID, so should be a no-op */
527
if( strcmp( id, "all" ) )
530
tr_bool isList = strchr(id,',') || strchr(id,'-');
531
tr_bool isNum = TRUE;
532
for( pch=id; isNum && *pch; ++pch )
533
if( !isdigit( *pch ) )
535
if( isNum || isList )
536
tr_rpc_parse_list_str( tr_bencDictAdd( args, "ids" ), id, strlen( id ) );
538
tr_bencDictAddStr( args, "ids", id ); /* it's a torrent sha hash */
543
addTime( tr_benc * args, const char * key, const char * arg )
546
tr_bool success = FALSE;
548
if( arg && ( strlen( arg ) == 4 ) )
550
const char hh[3] = { arg[0], arg[1], '\0' };
551
const char mm[3] = { arg[2], arg[3], '\0' };
552
const int hour = atoi( hh );
553
const int min = atoi( mm );
555
if( 0<=hour && hour<24 && 0<=min && min<60 )
557
time = min + ( hour * 60 );
563
tr_bencDictAddInt( args, key, time );
565
fprintf( stderr, "Please specify the time of day in 'hhmm' format.\n" );
569
addDays( tr_benc * args, const char * key, const char * arg )
577
int * values = tr_parseNumberRange( arg, -1, &valueCount );
578
for( i=0; i<valueCount; ++i )
580
if ( values[i] < 0 || values[i] > 7 ) continue;
581
if ( values[i] == 7 ) values[i] = 0;
583
days |= 1 << values[i];
589
tr_bencDictAddInt( args, key, days );
591
fprintf( stderr, "Please specify the days of the week in '1-3,4,7' format.\n" );
595
addFiles( tr_benc * args,
599
tr_benc * files = tr_bencDictAddList( args, key, 100 );
603
fprintf( stderr, "No files specified!\n" );
604
arg = "-1"; /* no file will have this index, so should be a no-op */
606
if( strcmp( arg, "all" ) )
610
int * values = tr_parseNumberRange( arg, -1, &valueCount );
611
for( i=0; i<valueCount; ++i )
612
tr_bencListAddInt( files, values[i] );
617
#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) )
619
static const char * files_keys[] = {
626
static const char * details_keys[] = {
646
"honorsSessionLimits",
653
"peersGettingFromUs",
671
"webseedsSendingToUs"
674
static const char * list_keys[] = {
682
"peersGettingFromUs",
692
writeFunc( void * ptr, size_t size, size_t nmemb, void * buf )
694
const size_t byteCount = size * nmemb;
695
evbuffer_add( buf, ptr, byteCount );
699
/* look for a session id in the header in case the server gives back a 409 */
701
parseResponseHeader( void *ptr, size_t size, size_t nmemb, void * stream UNUSED )
703
const char * line = ptr;
704
const size_t line_len = size * nmemb;
705
const char * key = TR_RPC_SESSION_ID_HEADER ": ";
706
const size_t key_len = strlen( key );
708
if( ( line_len >= key_len ) && !memcmp( line, key, key_len ) )
710
const char * begin = line + key_len;
711
const char * end = begin;
712
while( !isspace( *end ) )
714
tr_free( sessionId );
715
sessionId = tr_strndup( begin, end-begin );
722
getTimeoutSecs( const char * req )
724
if( strstr( req, "\"method\":\"blocklist-update\"" ) != NULL )
727
return 60L; /* default value */
731
getStatusString( tr_benc * t, char * buf, size_t buflen )
736
if( !tr_bencDictFindInt( t, "status", &status ) )
740
else switch( status )
742
case TR_STATUS_STOPPED:
743
if( tr_bencDictFindBool( t, "isFinished", &boolVal ) && boolVal )
744
tr_strlcpy( buf, "Finished", buflen );
746
tr_strlcpy( buf, "Stopped", buflen );
749
case TR_STATUS_CHECK_WAIT:
750
case TR_STATUS_CHECK: {
751
const char * str = status == TR_STATUS_CHECK_WAIT
755
if( tr_bencDictFindReal( t, "recheckProgress", &percent ) )
756
tr_snprintf( buf, buflen, "%s (%.0f%%)", str, floor(percent*100.0) );
758
tr_strlcpy( buf, str, buflen );
763
case TR_STATUS_DOWNLOAD:
764
case TR_STATUS_SEED: {
767
tr_bencDictFindInt( t, "peersGettingFromUs", &fromUs );
768
tr_bencDictFindInt( t, "peersSendingToUs", &toUs );
770
tr_strlcpy( buf, "Up & Down", buflen );
772
tr_strlcpy( buf, "Downloading", buflen );
774
int64_t leftUntilDone = 0;
775
tr_bencDictFindInt( t, "leftUntilDone", &leftUntilDone );
776
if( leftUntilDone > 0 )
777
tr_strlcpy( buf, "Uploading", buflen );
779
tr_strlcpy( buf, "Seeding", buflen );
781
tr_strlcpy( buf, "Idle", buflen );
790
static const char *bandwidthPriorityNames[] =
791
{ "Low", "Normal", "High", "Invalid" };
794
printDetails( tr_benc * top )
796
tr_benc *args, *torrents;
798
if( ( tr_bencDictFindDict( top, "arguments", &args ) )
799
&& ( tr_bencDictFindList( args, "torrents", &torrents ) ) )
802
for( ti = 0, tCount = tr_bencListSize( torrents ); ti < tCount;
805
tr_benc * t = tr_bencListChild( torrents, ti );
815
if( tr_bencDictFindInt( t, "id", &i ) )
816
printf( " Id: %" PRId64 "\n", i );
817
if( tr_bencDictFindStr( t, "name", &str ) )
818
printf( " Name: %s\n", str );
819
if( tr_bencDictFindStr( t, "hashString", &str ) )
820
printf( " Hash: %s\n", str );
823
printf( "TRANSFER\n" );
824
getStatusString( t, buf, sizeof( buf ) );
825
printf( " State: %s\n", buf );
827
if( tr_bencDictFindStr( t, "downloadDir", &str ) )
828
printf( " Location: %s\n", str );
830
if( tr_bencDictFindInt( t, "sizeWhenDone", &i )
831
&& tr_bencDictFindInt( t, "leftUntilDone", &j ) )
833
strlpercent( buf, 100.0 * ( i - j ) / i, sizeof( buf ) );
834
printf( " Percent Done: %s%%\n", buf );
837
if( tr_bencDictFindInt( t, "eta", &i ) )
838
printf( " ETA: %s\n", tr_strltime( buf, i, sizeof( buf ) ) );
839
if( tr_bencDictFindInt( t, "rateDownload", &i ) )
840
printf( " Download Speed: %s\n", tr_formatter_speed_KBps( buf, i/(double)tr_speed_K, sizeof( buf ) ) );
841
if( tr_bencDictFindInt( t, "rateUpload", &i ) )
842
printf( " Upload Speed: %s\n", tr_formatter_speed_KBps( buf, i/(double)tr_speed_K, sizeof( buf ) ) );
843
if( tr_bencDictFindInt( t, "haveUnchecked", &i )
844
&& tr_bencDictFindInt( t, "haveValid", &j ) )
846
strlsize( buf, i + j, sizeof( buf ) );
847
strlsize( buf2, j, sizeof( buf2 ) );
848
printf( " Have: %s (%s verified)\n", buf, buf2 );
851
if( tr_bencDictFindInt( t, "sizeWhenDone", &i ) )
854
printf( " Availability: None\n" );
855
if( tr_bencDictFindInt( t, "desiredAvailable", &j)
856
&& tr_bencDictFindInt( t, "leftUntilDone", &k) )
859
strlpercent( buf, 100.0 * j / i, sizeof( buf ) );
860
printf( " Availability: %s%%\n", buf );
862
if( tr_bencDictFindInt( t, "totalSize", &j ) )
864
strlsize( buf2, i, sizeof( buf2 ) );
865
strlsize( buf, j, sizeof( buf ) );
866
printf( " Total size: %s (%s wanted)\n", buf, buf2 );
869
if( tr_bencDictFindInt( t, "downloadedEver", &i )
870
&& tr_bencDictFindInt( t, "uploadedEver", &j ) )
872
strlsize( buf, i, sizeof( buf ) );
873
printf( " Downloaded: %s\n", buf );
874
strlsize( buf, j, sizeof( buf ) );
875
printf( " Uploaded: %s\n", buf );
876
strlratio( buf, j, i, sizeof( buf ) );
877
printf( " Ratio: %s\n", buf );
879
if( tr_bencDictFindInt( t, "seedRatioMode", &i))
882
case TR_RATIOLIMIT_GLOBAL:
883
printf( " Ratio Limit: Default\n" );
885
case TR_RATIOLIMIT_SINGLE:
886
if( tr_bencDictFindReal( t, "seedRatioLimit", &d))
887
printf( " Ratio Limit: %.2f\n", d );
889
case TR_RATIOLIMIT_UNLIMITED:
890
printf( " Ratio Limit: Unlimited\n" );
895
if( tr_bencDictFindInt( t, "corruptEver", &i ) )
897
strlsize( buf, i, sizeof( buf ) );
898
printf( " Corrupt DL: %s\n", buf );
900
if( tr_bencDictFindStr( t, "errorString", &str ) && str && *str &&
901
tr_bencDictFindInt( t, "error", &i ) && i )
904
case TR_STAT_TRACKER_WARNING: printf( " Tracker gave a warning: %s\n", str ); break;
905
case TR_STAT_TRACKER_ERROR: printf( " Tracker gave an error: %s\n", str ); break;
906
case TR_STAT_LOCAL_ERROR: printf( " Error: %s\n", str ); break;
907
default: break; /* no error */
910
if( tr_bencDictFindInt( t, "peersConnected", &i )
911
&& tr_bencDictFindInt( t, "peersGettingFromUs", &j )
912
&& tr_bencDictFindInt( t, "peersSendingToUs", &k ) )
916
"connected to %" PRId64 ", "
917
"uploading to %" PRId64
924
if( tr_bencDictFindList( t, "webseeds", &l )
925
&& tr_bencDictFindInt( t, "webseedsSendingToUs", &i ) )
927
const int64_t n = tr_bencListSize( l );
930
" Web Seeds: downloading from %" PRId64 " of %"
932
" web seeds\n", i, n );
936
printf( "HISTORY\n" );
937
if( tr_bencDictFindInt( t, "addedDate", &i ) && i )
940
printf( " Date added: %s", ctime( &tt ) );
942
if( tr_bencDictFindInt( t, "doneDate", &i ) && i )
945
printf( " Date finished: %s", ctime( &tt ) );
947
if( tr_bencDictFindInt( t, "startDate", &i ) && i )
950
printf( " Date started: %s", ctime( &tt ) );
952
if( tr_bencDictFindInt( t, "activityDate", &i ) && i )
955
printf( " Latest activity: %s", ctime( &tt ) );
959
printf( "ORIGINS\n" );
960
if( tr_bencDictFindInt( t, "dateCreated", &i ) && i )
963
printf( " Date created: %s", ctime( &tt ) );
965
if( tr_bencDictFindBool( t, "isPrivate", &boolVal ) )
966
printf( " Public torrent: %s\n", ( boolVal ? "No" : "Yes" ) );
967
if( tr_bencDictFindStr( t, "comment", &str ) && str && *str )
968
printf( " Comment: %s\n", str );
969
if( tr_bencDictFindStr( t, "creator", &str ) && str && *str )
970
printf( " Creator: %s\n", str );
971
if( tr_bencDictFindInt( t, "pieceCount", &i ) )
972
printf( " Piece Count: %" PRId64 "\n", i );
973
if( tr_bencDictFindInt( t, "pieceSize", &i ) )
974
printf( " Piece Size: %s\n", strlmem( buf, i, sizeof( buf ) ) );
977
printf( "LIMITS & BANDWIDTH\n" );
978
if( tr_bencDictFindBool( t, "downloadLimited", &boolVal )
979
&& tr_bencDictFindInt( t, "downloadLimit", &i ) )
981
printf( " Download Limit: " );
983
printf( "%s\n", tr_formatter_speed_KBps( buf, i, sizeof( buf ) ) );
985
printf( "Unlimited\n" );
987
if( tr_bencDictFindBool( t, "uploadLimited", &boolVal )
988
&& tr_bencDictFindInt( t, "uploadLimit", &i ) )
990
printf( " Upload Limit: " );
992
printf( "%s\n", tr_formatter_speed_KBps( buf, i, sizeof( buf ) ) );
994
printf( "Unlimited\n" );
996
if( tr_bencDictFindBool( t, "honorsSessionLimits", &boolVal ) )
997
printf( " Honors Session Limits: %s\n", ( boolVal ? "Yes" : "No" ) );
998
if( tr_bencDictFindInt ( t, "peer-limit", &i ) )
999
printf( " Peer limit: %" PRId64 "\n", i );
1000
if (tr_bencDictFindInt (t, "bandwidthPriority", &i))
1001
printf (" Bandwidth Priority: %s\n",
1002
bandwidthPriorityNames[(i + 1) & 3]);
1010
printFileList( tr_benc * top )
1012
tr_benc *args, *torrents;
1014
if( ( tr_bencDictFindDict( top, "arguments", &args ) )
1015
&& ( tr_bencDictFindList( args, "torrents", &torrents ) ) )
1018
for( i = 0, in = tr_bencListSize( torrents ); i < in; ++i )
1020
tr_benc * d = tr_bencListChild( torrents, i );
1021
tr_benc * files, *priorities, *wanteds;
1023
if( tr_bencDictFindStr( d, "name", &name )
1024
&& tr_bencDictFindList( d, "files", &files )
1025
&& tr_bencDictFindList( d, "priorities", &priorities )
1026
&& tr_bencDictFindList( d, "wanted", &wanteds ) )
1028
int j = 0, jn = tr_bencListSize( files );
1029
printf( "%s (%d files):\n", name, jn );
1030
printf( "%3s %4s %8s %3s %9s %s\n", "#", "Done",
1031
"Priority", "Get", "Size",
1033
for( j = 0, jn = tr_bencListSize( files ); j < jn; ++j )
1039
const char * filename;
1040
tr_benc * file = tr_bencListChild( files, j );
1041
if( tr_bencDictFindInt( file, "length", &length )
1042
&& tr_bencDictFindStr( file, "name", &filename )
1043
&& tr_bencDictFindInt( file, "bytesCompleted", &have )
1044
&& tr_bencGetInt( tr_bencListChild( priorities,
1046
&& tr_bencGetInt( tr_bencListChild( wanteds,
1050
double percent = (double)have / length;
1051
const char * pristr;
1052
strlsize( sizestr, length, sizeof( sizestr ) );
1056
pristr = "Low"; break;
1059
pristr = "High"; break;
1062
pristr = "Normal"; break;
1064
printf( "%3d: %3.0f%% %-8s %-3s %9s %s\n",
1066
floor( 100.0 * percent ),
1068
( wanted ? "Yes" : "No" ),
1079
printPeersImpl( tr_benc * peers )
1082
printf( "%-20s %-12s %-5s %-6s %-6s %s\n",
1083
"Address", "Flags", "Done", "Down", "Up", "Client" );
1084
for( i = 0, n = tr_bencListSize( peers ); i < n; ++i )
1087
const char * address, * client, * flagstr;
1088
int64_t rateToClient, rateToPeer;
1089
tr_benc * d = tr_bencListChild( peers, i );
1091
if( tr_bencDictFindStr( d, "address", &address )
1092
&& tr_bencDictFindStr( d, "clientName", &client )
1093
&& tr_bencDictFindReal( d, "progress", &progress )
1094
&& tr_bencDictFindStr( d, "flagStr", &flagstr )
1095
&& tr_bencDictFindInt( d, "rateToClient", &rateToClient )
1096
&& tr_bencDictFindInt( d, "rateToPeer", &rateToPeer ) )
1098
printf( "%-20s %-12s %-5.1f %6.1f %6.1f %s\n",
1099
address, flagstr, (progress*100.0),
1100
rateToClient / (double)tr_speed_K,
1101
rateToPeer / (double)tr_speed_K,
1108
printPeers( tr_benc * top )
1110
tr_benc *args, *torrents;
1112
if( tr_bencDictFindDict( top, "arguments", &args )
1113
&& tr_bencDictFindList( args, "torrents", &torrents ) )
1116
for( i=0, n=tr_bencListSize( torrents ); i<n; ++i )
1119
tr_benc * torrent = tr_bencListChild( torrents, i );
1120
if( tr_bencDictFindList( torrent, "peers", &peers ) ) {
1121
printPeersImpl( peers );
1130
printPiecesImpl( const uint8_t * raw, size_t rawlen, int64_t j )
1133
char * str = tr_base64_decode( raw, rawlen, &len );
1135
for( i=k=0; k<len; ++k ) {
1137
for( e=0; i<j && e<8; ++e, ++i )
1138
printf( "%c", str[k] & (1<<(7-e)) ? '1' : '0' );
1148
printPieces( tr_benc * top )
1150
tr_benc *args, *torrents;
1152
if( tr_bencDictFindDict( top, "arguments", &args )
1153
&& tr_bencDictFindList( args, "torrents", &torrents ) )
1156
for( i=0, n=tr_bencListSize( torrents ); i<n; ++i )
1159
const uint8_t * raw;
1161
tr_benc * torrent = tr_bencListChild( torrents, i );
1162
if( tr_bencDictFindRaw( torrent, "pieces", &raw, &rawlen ) &&
1163
tr_bencDictFindInt( torrent, "pieceCount", &j ) ) {
1164
printPiecesImpl( raw, rawlen, j );
1173
printPortTest( tr_benc * top )
1176
if( ( tr_bencDictFindDict( top, "arguments", &args ) ) )
1180
if( tr_bencDictFindBool( args, "port-is-open", &boolVal ) )
1181
printf( "Port is open: %s\n", ( boolVal ? "Yes" : "No" ) );
1186
printTorrentList( tr_benc * top )
1188
tr_benc *args, *list;
1190
if( ( tr_bencDictFindDict( top, "arguments", &args ) )
1191
&& ( tr_bencDictFindList( args, "torrents", &list ) ) )
1194
int64_t total_size=0;
1195
double total_up=0, total_down=0;
1198
printf( "%-4s %-4s %9s %-8s %6s %6s %-5s %-11s %s\n",
1199
"ID", "Done", "Have", "ETA", "Up", "Down", "Ratio", "Status",
1202
for( i = 0, n = tr_bencListSize( list ); i < n; ++i )
1204
int64_t id, eta, status, up, down;
1205
int64_t sizeWhenDone, leftUntilDone;
1208
tr_benc * d = tr_bencListChild( list, i );
1209
if( tr_bencDictFindInt( d, "eta", &eta )
1210
&& tr_bencDictFindInt( d, "id", &id )
1211
&& tr_bencDictFindInt( d, "leftUntilDone", &leftUntilDone )
1212
&& tr_bencDictFindStr( d, "name", &name )
1213
&& tr_bencDictFindInt( d, "rateDownload", &down )
1214
&& tr_bencDictFindInt( d, "rateUpload", &up )
1215
&& tr_bencDictFindInt( d, "sizeWhenDone", &sizeWhenDone )
1216
&& tr_bencDictFindInt( d, "status", &status )
1217
&& tr_bencDictFindReal( d, "uploadRatio", &ratio ) )
1227
tr_snprintf( doneStr, sizeof( doneStr ), "%d%%", (int)( 100.0 * ( sizeWhenDone - leftUntilDone ) / sizeWhenDone ) );
1229
tr_strlcpy( doneStr, "n/a", sizeof( doneStr ) );
1231
strlsize( haveStr, sizeWhenDone - leftUntilDone, sizeof( haveStr ) );
1233
if( leftUntilDone || eta != -1 )
1234
etaToString( etaStr, sizeof( etaStr ), eta );
1236
tr_snprintf( etaStr, sizeof( etaStr ), "Done" );
1237
if( tr_bencDictFindInt( d, "error", &error ) && error )
1242
"%4d%c %4s %9s %-8s %6.1f %6.1f %5s %-11s %s\n",
1247
up/(double)tr_speed_K,
1248
down/(double)tr_speed_K,
1249
strlratio2( ratioStr, ratio, sizeof( ratioStr ) ),
1250
getStatusString( d, statusStr, sizeof( statusStr ) ),
1255
total_size += sizeWhenDone - leftUntilDone;
1259
printf( "Sum: %9s %6.1f %6.1f\n",
1260
strlsize( haveStr, total_size, sizeof( haveStr ) ),
1261
total_up/(double)tr_speed_K,
1262
total_down/(double)tr_speed_K );
1267
printTrackersImpl( tr_benc * trackerStats )
1273
for( i=0; (( t = tr_bencListChild( trackerStats, i ))); ++i )
1275
int64_t downloadCount;
1276
tr_bool hasAnnounced;
1281
int64_t lastAnnouncePeerCount;
1282
const char * lastAnnounceResult;
1283
int64_t lastAnnounceStartTime;
1284
tr_bool lastAnnounceSucceeded;
1285
int64_t lastAnnounceTime;
1286
tr_bool lastAnnounceTimedOut;
1287
const char * lastScrapeResult;
1288
tr_bool lastScrapeSucceeded;
1289
int64_t lastScrapeStartTime;
1290
int64_t lastScrapeTime;
1291
tr_bool lastScrapeTimedOut;
1292
int64_t leecherCount;
1293
int64_t nextAnnounceTime;
1294
int64_t nextScrapeTime;
1295
int64_t seederCount;
1297
int64_t announceState;
1298
int64_t scrapeState;
1300
if( tr_bencDictFindInt ( t, "downloadCount", &downloadCount ) &&
1301
tr_bencDictFindBool( t, "hasAnnounced", &hasAnnounced ) &&
1302
tr_bencDictFindBool( t, "hasScraped", &hasScraped ) &&
1303
tr_bencDictFindStr ( t, "host", &host ) &&
1304
tr_bencDictFindInt ( t, "id", &id ) &&
1305
tr_bencDictFindBool( t, "isBackup", &isBackup ) &&
1306
tr_bencDictFindInt ( t, "announceState", &announceState ) &&
1307
tr_bencDictFindInt ( t, "scrapeState", &scrapeState ) &&
1308
tr_bencDictFindInt ( t, "lastAnnouncePeerCount", &lastAnnouncePeerCount ) &&
1309
tr_bencDictFindStr ( t, "lastAnnounceResult", &lastAnnounceResult ) &&
1310
tr_bencDictFindInt ( t, "lastAnnounceStartTime", &lastAnnounceStartTime ) &&
1311
tr_bencDictFindBool( t, "lastAnnounceSucceeded", &lastAnnounceSucceeded ) &&
1312
tr_bencDictFindInt ( t, "lastAnnounceTime", &lastAnnounceTime ) &&
1313
tr_bencDictFindBool( t, "lastAnnounceTimedOut", &lastAnnounceTimedOut ) &&
1314
tr_bencDictFindStr ( t, "lastScrapeResult", &lastScrapeResult ) &&
1315
tr_bencDictFindInt ( t, "lastScrapeStartTime", &lastScrapeStartTime ) &&
1316
tr_bencDictFindBool( t, "lastScrapeSucceeded", &lastScrapeSucceeded ) &&
1317
tr_bencDictFindInt ( t, "lastScrapeTime", &lastScrapeTime ) &&
1318
tr_bencDictFindBool( t, "lastScrapeTimedOut", &lastScrapeTimedOut ) &&
1319
tr_bencDictFindInt ( t, "leecherCount", &leecherCount ) &&
1320
tr_bencDictFindInt ( t, "nextAnnounceTime", &nextAnnounceTime ) &&
1321
tr_bencDictFindInt ( t, "nextScrapeTime", &nextScrapeTime ) &&
1322
tr_bencDictFindInt ( t, "seederCount", &seederCount ) &&
1323
tr_bencDictFindInt ( t, "tier", &tier ) )
1325
const time_t now = time( NULL );
1328
printf( " Tracker %d: %s\n", (int)(id), host );
1330
printf( " Backup on tier %d\n", (int)tier );
1332
printf( " Active in tier %d\n", (int)tier );
1336
if( hasAnnounced && announceState != TR_TRACKER_INACTIVE )
1338
tr_strltime( buf, now - lastAnnounceTime, sizeof( buf ) );
1339
if( lastAnnounceSucceeded )
1340
printf( " Got a list of %'d peers %s ago\n",
1341
(int)lastAnnouncePeerCount, buf );
1342
else if( lastAnnounceTimedOut )
1343
printf( " Peer list request timed out; will retry\n" );
1345
printf( " Got an error \"%s\" %s ago\n",
1346
lastAnnounceResult, buf );
1349
switch( announceState )
1351
case TR_TRACKER_INACTIVE:
1352
printf( " No updates scheduled\n" );
1354
case TR_TRACKER_WAITING:
1355
tr_strltime( buf, nextAnnounceTime - now, sizeof( buf ) );
1356
printf( " Asking for more peers in %s\n", buf );
1358
case TR_TRACKER_QUEUED:
1359
printf( " Queued to ask for more peers\n" );
1361
case TR_TRACKER_ACTIVE:
1362
tr_strltime( buf, now - lastAnnounceStartTime, sizeof( buf ) );
1363
printf( " Asking for more peers now... %s\n", buf );
1369
tr_strltime( buf, now - lastScrapeTime, sizeof( buf ) );
1370
if( lastScrapeSucceeded )
1371
printf( " Tracker had %'d seeders and %'d leechers %s ago\n",
1372
(int)seederCount, (int)leecherCount, buf );
1373
else if( lastScrapeTimedOut )
1374
printf( " Tracker scrape timed out; will retry\n" );
1376
printf( " Got a scrape error \"%s\" %s ago\n",
1377
lastScrapeResult, buf );
1380
switch( scrapeState )
1382
case TR_TRACKER_INACTIVE:
1384
case TR_TRACKER_WAITING:
1385
tr_strltime( buf, nextScrapeTime - now, sizeof( buf ) );
1386
printf( " Asking for peer counts in %s\n", buf );
1388
case TR_TRACKER_QUEUED:
1389
printf( " Queued to ask for peer counts\n" );
1391
case TR_TRACKER_ACTIVE:
1392
tr_strltime( buf, now - lastScrapeStartTime, sizeof( buf ) );
1393
printf( " Asking for peer counts now... %s\n", buf );
1402
printTrackers( tr_benc * top )
1404
tr_benc *args, *torrents;
1406
if( tr_bencDictFindDict( top, "arguments", &args )
1407
&& tr_bencDictFindList( args, "torrents", &torrents ) )
1410
for( i=0, n=tr_bencListSize( torrents ); i<n; ++i )
1412
tr_benc * trackerStats;
1413
tr_benc * torrent = tr_bencListChild( torrents, i );
1414
if( tr_bencDictFindList( torrent, "trackerStats", &trackerStats ) ) {
1415
printTrackersImpl( trackerStats );
1424
printSession( tr_benc * top )
1427
if( ( tr_bencDictFindDict( top, "arguments", &args ) ) )
1434
printf( "VERSION\n" );
1435
if( tr_bencDictFindStr( args, "version", &str ) )
1436
printf( " Daemon version: %s\n", str );
1437
if( tr_bencDictFindInt( args, "rpc-version", &i ) )
1438
printf( " RPC version: %" PRId64 "\n", i );
1439
if( tr_bencDictFindInt( args, "rpc-version-minimum", &i ) )
1440
printf( " RPC minimum version: %" PRId64 "\n", i );
1443
printf( "CONFIG\n" );
1444
if( tr_bencDictFindStr( args, "config-dir", &str ) )
1445
printf( " Configuration directory: %s\n", str );
1446
if( tr_bencDictFindStr( args, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1447
printf( " Download directory: %s\n", str );
1448
if( tr_bencDictFindInt( args, TR_PREFS_KEY_PEER_PORT, &i ) )
1449
printf( " Listenport: %" PRId64 "\n", i );
1450
if( tr_bencDictFindBool( args, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) )
1451
printf( " Portforwarding enabled: %s\n", ( boolVal ? "Yes" : "No" ) );
1452
if( tr_bencDictFindBool( args, TR_PREFS_KEY_DHT_ENABLED, &boolVal ) )
1453
printf( " Distributed hash table enabled: %s\n", ( boolVal ? "Yes" : "No" ) );
1454
if( tr_bencDictFindBool( args, TR_PREFS_KEY_LPD_ENABLED, &boolVal ) )
1455
printf( " Local peer discovery enabled: %s\n", ( boolVal ? "Yes" : "No" ) );
1456
if( tr_bencDictFindBool( args, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) )
1457
printf( " Peer exchange allowed: %s\n", ( boolVal ? "Yes" : "No" ) );
1458
if( tr_bencDictFindStr( args, TR_PREFS_KEY_ENCRYPTION, &str ) )
1459
printf( " Encryption: %s\n", str );
1460
if( tr_bencDictFindInt( args, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, &i ) )
1461
printf( " Maximum memory cache size: %s\n", tr_formatter_mem_MB( buf, i, sizeof( buf ) ) );
1465
tr_bool altEnabled, altTimeEnabled, upEnabled, downEnabled, seedRatioLimited;
1466
int64_t altDown, altUp, altBegin, altEnd, altDay, upLimit, downLimit, peerLimit;
1467
double seedRatioLimit;
1469
if( tr_bencDictFindInt ( args, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, &altDown ) &&
1470
tr_bencDictFindBool( args, TR_PREFS_KEY_ALT_SPEED_ENABLED, &altEnabled ) &&
1471
tr_bencDictFindInt ( args, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &altBegin ) &&
1472
tr_bencDictFindBool( args, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &altTimeEnabled ) &&
1473
tr_bencDictFindInt ( args, TR_PREFS_KEY_ALT_SPEED_TIME_END, &altEnd ) &&
1474
tr_bencDictFindInt ( args, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &altDay ) &&
1475
tr_bencDictFindInt ( args, TR_PREFS_KEY_ALT_SPEED_UP_KBps, &altUp ) &&
1476
tr_bencDictFindInt ( args, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &peerLimit ) &&
1477
tr_bencDictFindInt ( args, TR_PREFS_KEY_DSPEED_KBps, &downLimit ) &&
1478
tr_bencDictFindBool( args, TR_PREFS_KEY_DSPEED_ENABLED, &downEnabled ) &&
1479
tr_bencDictFindInt ( args, TR_PREFS_KEY_USPEED_KBps, &upLimit ) &&
1480
tr_bencDictFindBool( args, TR_PREFS_KEY_USPEED_ENABLED, &upEnabled ) &&
1481
tr_bencDictFindReal( args, "seedRatioLimit", &seedRatioLimit ) &&
1482
tr_bencDictFindBool( args, "seedRatioLimited", &seedRatioLimited) )
1488
printf( "LIMITS\n" );
1489
printf( " Peer limit: %" PRId64 "\n", peerLimit );
1491
if( seedRatioLimited )
1492
tr_snprintf( buf, sizeof( buf ), "%.2f", seedRatioLimit );
1494
tr_strlcpy( buf, "Unlimited", sizeof( buf ) );
1495
printf( " Default seed ratio limit: %s\n", buf );
1498
tr_formatter_speed_KBps( buf, altUp, sizeof( buf ) );
1499
else if( upEnabled )
1500
tr_formatter_speed_KBps( buf, upLimit, sizeof( buf ) );
1502
tr_strlcpy( buf, "Unlimited", sizeof( buf ) );
1503
printf( " Upload speed limit: %s (%s limit: %s; %s turtle limit: %s)\n",
1505
upEnabled ? "Enabled" : "Disabled",
1506
tr_formatter_speed_KBps( buf2, upLimit, sizeof( buf2 ) ),
1507
altEnabled ? "Enabled" : "Disabled",
1508
tr_formatter_speed_KBps( buf3, altUp, sizeof( buf3 ) ) );
1511
tr_formatter_speed_KBps( buf, altDown, sizeof( buf ) );
1512
else if( downEnabled )
1513
tr_formatter_speed_KBps( buf, downLimit, sizeof( buf ) );
1515
tr_strlcpy( buf, "Unlimited", sizeof( buf ) );
1516
printf( " Download speed limit: %s (%s limit: %s; %s turtle limit: %s)\n",
1518
downEnabled ? "Enabled" : "Disabled",
1519
tr_formatter_speed_KBps( buf2, downLimit, sizeof( buf2 ) ),
1520
altEnabled ? "Enabled" : "Disabled",
1521
tr_formatter_speed_KBps( buf2, altDown, sizeof( buf2 ) ) );
1523
if( altTimeEnabled ) {
1524
printf( " Turtle schedule: %02d:%02d - %02d:%02d ",
1525
(int)(altBegin/60), (int)(altBegin%60),
1526
(int)(altEnd/60), (int)(altEnd%60) );
1527
if( altDay & TR_SCHED_SUN ) printf( "Sun " );
1528
if( altDay & TR_SCHED_MON ) printf( "Mon " );
1529
if( altDay & TR_SCHED_TUES ) printf( "Tue " );
1530
if( altDay & TR_SCHED_WED ) printf( "Wed " );
1531
if( altDay & TR_SCHED_THURS ) printf( "Thu " );
1532
if( altDay & TR_SCHED_FRI ) printf( "Fri " );
1533
if( altDay & TR_SCHED_SAT ) printf( "Sat " );
1541
if( tr_bencDictFindBool( args, TR_PREFS_KEY_START, &boolVal ) )
1542
printf( " Autostart added torrents: %s\n", ( boolVal ? "Yes" : "No" ) );
1543
if( tr_bencDictFindBool( args, TR_PREFS_KEY_TRASH_ORIGINAL, &boolVal ) )
1544
printf( " Delete automatically added torrents: %s\n", ( boolVal ? "Yes" : "No" ) );
1549
printSessionStats( tr_benc * top )
1552
if( ( tr_bencDictFindDict( top, "arguments", &args ) ) )
1555
int64_t up, down, secs, sessions;
1557
if( tr_bencDictFindDict( args, "current-stats", &d )
1558
&& tr_bencDictFindInt( d, "uploadedBytes", &up )
1559
&& tr_bencDictFindInt( d, "downloadedBytes", &down )
1560
&& tr_bencDictFindInt( d, "secondsActive", &secs ) )
1562
printf( "\nCURRENT SESSION\n" );
1563
printf( " Uploaded: %s\n", strlsize( buf, up, sizeof( buf ) ) );
1564
printf( " Downloaded: %s\n", strlsize( buf, down, sizeof( buf ) ) );
1565
printf( " Ratio: %s\n", strlratio( buf, up, down, sizeof( buf ) ) );
1566
printf( " Duration: %s\n", tr_strltime( buf, secs, sizeof( buf ) ) );
1569
if( tr_bencDictFindDict( args, "cumulative-stats", &d )
1570
&& tr_bencDictFindInt( d, "sessionCount", &sessions )
1571
&& tr_bencDictFindInt( d, "uploadedBytes", &up )
1572
&& tr_bencDictFindInt( d, "downloadedBytes", &down )
1573
&& tr_bencDictFindInt( d, "secondsActive", &secs ) )
1575
printf( "\nTOTAL\n" );
1576
printf( " Started %lu times\n", (unsigned long)sessions );
1577
printf( " Uploaded: %s\n", strlsize( buf, up, sizeof( buf ) ) );
1578
printf( " Downloaded: %s\n", strlsize( buf, down, sizeof( buf ) ) );
1579
printf( " Ratio: %s\n", strlratio( buf, up, down, sizeof( buf ) ) );
1580
printf( " Duration: %s\n", tr_strltime( buf, secs, sizeof( buf ) ) );
1585
static char id[4096];
1588
processResponse( const char * host, int port, const void * response, size_t len )
1591
int status = EXIT_SUCCESS;
1594
fprintf( stderr, "got response (len %d):\n--------\n%*.*s\n--------\n",
1595
(int)len, (int)len, (int)len, (const char*) response );
1597
if( tr_jsonParse( NULL, response, len, &top, NULL ) )
1599
tr_nerr( MY_NAME, "Unable to parse response \"%*.*s\"", (int)len,
1600
(int)len, (char*)response );
1601
status |= EXIT_FAILURE;
1607
tr_bencDictFindInt( &top, "tag", &tag );
1612
printSession( &top ); break;
1615
printSessionStats( &top ); break;
1618
printDetails( &top ); break;
1621
printFileList( &top ); break;
1624
printTorrentList( &top ); break;
1627
printPeers( &top ); break;
1630
printPieces( &top ); break;
1633
printPortTest( &top ); break;
1636
printTrackers( &top ); break;
1638
case TAG_TORRENT_ADD: {
1641
if( tr_bencDictFindDict( &top, ARGUMENTS, &b )
1642
&& tr_bencDictFindDict( b, "torrent-added", &b )
1643
&& tr_bencDictFindInt( b, "id", &i ) )
1644
tr_snprintf( id, sizeof(id), "%"PRId64, i );
1645
/* fall-through to default: to give success or failure msg */
1648
if( !tr_bencDictFindStr( &top, "result", &str ) )
1649
status |= EXIT_FAILURE;
1651
printf( "%s:%d responded: \"%s\"\n", host, port, str );
1652
if( strcmp( str, "success") )
1653
status |= EXIT_FAILURE;
1657
tr_bencFree( &top );
1664
tr_curl_easy_init( struct evbuffer * writebuf )
1666
CURL * curl = curl_easy_init( );
1667
curl_easy_setopt( curl, CURLOPT_USERAGENT, MY_NAME "/" LONG_VERSION_STRING );
1668
curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, writeFunc );
1669
curl_easy_setopt( curl, CURLOPT_WRITEDATA, writebuf );
1670
curl_easy_setopt( curl, CURLOPT_HEADERFUNCTION, parseResponseHeader );
1671
curl_easy_setopt( curl, CURLOPT_POST, 1 );
1672
curl_easy_setopt( curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL );
1673
curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
1674
curl_easy_setopt( curl, CURLOPT_VERBOSE, debug );
1675
curl_easy_setopt( curl, CURLOPT_ENCODING, "" ); /* "" tells curl to fill in the blanks with what it was compiled to support */
1677
curl_easy_setopt( curl, CURLOPT_NETRC_FILE, netrc );
1679
curl_easy_setopt( curl, CURLOPT_USERPWD, auth );
1681
char * h = tr_strdup_printf( "%s: %s", TR_RPC_SESSION_ID_HEADER, sessionId );
1682
struct curl_slist * custom_headers = curl_slist_append( NULL, h );
1683
curl_easy_setopt( curl, CURLOPT_HTTPHEADER, custom_headers );
1690
flush( const char * host, int port, tr_benc ** benc )
1694
int status = EXIT_SUCCESS;
1695
struct evbuffer * buf = evbuffer_new( );
1696
char * json = tr_bencToStr( *benc, TR_FMT_JSON_LEAN, NULL );
1697
char * url = tr_strdup_printf( "http://%s:%d/transmission/rpc", host, port );
1699
curl = tr_curl_easy_init( buf );
1700
curl_easy_setopt( curl, CURLOPT_URL, url );
1701
curl_easy_setopt( curl, CURLOPT_POSTFIELDS, json );
1702
curl_easy_setopt( curl, CURLOPT_TIMEOUT, getTimeoutSecs( json ) );
1705
fprintf( stderr, "posting:\n--------\n%s\n--------\n", json );
1707
if(( res = curl_easy_perform( curl )))
1709
tr_nerr( MY_NAME, "(%s) %s", url, curl_easy_strerror( res ) );
1710
status |= EXIT_FAILURE;
1715
curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &response );
1716
switch( response ) {
1718
status |= processResponse( host, port, EVBUFFER_DATA(buf), EVBUFFER_LENGTH(buf) );
1721
/* session id failed. our curl header func has already
1722
* pulled the new session id from this response's headers,
1723
* build a new CURL* and try again */
1724
curl_easy_cleanup( curl );
1726
flush( host, port, benc );
1730
fprintf( stderr, "Unexpected response: %s\n", (char*)EVBUFFER_DATA(buf) );
1731
status |= EXIT_FAILURE;
1739
evbuffer_free( buf );
1741
curl_easy_cleanup( curl );
1742
if( benc != NULL ) {
1743
tr_bencFree( *benc );
1750
ensure_sset( tr_benc ** sset )
1755
args = tr_bencDictFind( *sset, ARGUMENTS );
1757
*sset = tr_new0( tr_benc, 1 );
1758
tr_bencInitDict( *sset, 3 );
1759
tr_bencDictAddStr( *sset, "method", "session-set" );
1760
args = tr_bencDictAddDict( *sset, ARGUMENTS, 0 );
1767
ensure_tset( tr_benc ** tset )
1772
args = tr_bencDictFind( *tset, ARGUMENTS );
1774
*tset = tr_new0( tr_benc, 1 );
1775
tr_bencInitDict( *tset, 3 );
1776
tr_bencDictAddStr( *tset, "method", "torrent-set" );
1777
args = tr_bencDictAddDict( *tset, ARGUMENTS, 1 );
1784
processArgs( const char * host, int port, int argc, const char ** argv )
1787
int status = EXIT_SUCCESS;
1788
const char * optarg;
1795
while(( c = tr_getopt( getUsage( ), argc, argv, opts, &optarg )))
1797
const int stepMode = getOptMode( c );
1799
if( !stepMode ) /* meta commands */
1803
case 'a': /* add torrent */
1804
if( sset != 0 ) status |= flush( host, port, &sset );
1805
if( tadd != 0 ) status |= flush( host, port, &tadd );
1806
if( tset != 0 ) { addIdArg( tr_bencDictFind( tset, ARGUMENTS ), id ); status |= flush( host, port, &tset ); }
1807
tadd = tr_new0( tr_benc, 1 );
1808
tr_bencInitDict( tadd, 3 );
1809
tr_bencDictAddStr( tadd, "method", "torrent-add" );
1810
tr_bencDictAddInt( tadd, "tag", TAG_TORRENT_ADD );
1811
tr_bencDictAddDict( tadd, ARGUMENTS, 0 );
1814
case 'b': /* debug */
1818
case 'n': /* auth */
1819
auth = tr_strdup( optarg );
1822
case 'N': /* netrc */
1823
netrc = tr_strdup( optarg );
1826
case 't': /* set current torrent */
1827
if( tadd != 0 ) status |= flush( host, port, &tadd );
1828
if( tset != 0 ) { addIdArg( tr_bencDictFind( tset, ARGUMENTS ), id ); status |= flush( host, port, &tset ); }
1829
tr_strlcpy( id, optarg, sizeof( id ) );
1832
case 'V': /* show version number */
1833
fprintf( stderr, "%s %s\n", MY_NAME, LONG_VERSION_STRING );
1838
fprintf( stderr, "invalid option\n" );
1840
status |= EXIT_FAILURE;
1845
tr_benc * args = tr_bencDictFind( tadd, ARGUMENTS );
1846
char * tmp = getEncodedMetainfo( optarg );
1848
tr_bencDictAddStr( args, "metainfo", tmp );
1850
tr_bencDictAddStr( args, "filename", optarg );
1853
fprintf( stderr, "Unknown option: %s\n", optarg );
1854
status |= EXIT_FAILURE;
1859
else if( stepMode == MODE_TORRENT_GET )
1862
tr_benc * top = tr_new0( tr_benc, 1 );
1865
tr_bencInitDict( top, 3 );
1866
tr_bencDictAddStr( top, "method", "torrent-get" );
1867
args = tr_bencDictAddDict( top, ARGUMENTS, 0 );
1868
fields = tr_bencDictAddList( args, "fields", 0 );
1870
if( tset != 0 ) { addIdArg( tr_bencDictFind( tset, ARGUMENTS ), id ); status |= flush( host, port, &tset ); }
1874
case 'i': tr_bencDictAddInt( top, "tag", TAG_DETAILS );
1875
n = TR_N_ELEMENTS( details_keys );
1876
for( i=0; i<n; ++i ) tr_bencListAddStr( fields, details_keys[i] );
1877
addIdArg( args, id );
1879
case 'l': tr_bencDictAddInt( top, "tag", TAG_LIST );
1880
n = TR_N_ELEMENTS( list_keys );
1881
for( i=0; i<n; ++i ) tr_bencListAddStr( fields, list_keys[i] );
1883
case 940: tr_bencDictAddInt( top, "tag", TAG_FILES );
1884
n = TR_N_ELEMENTS( files_keys );
1885
for( i=0; i<n; ++i ) tr_bencListAddStr( fields, files_keys[i] );
1886
addIdArg( args, id );
1888
case 941: tr_bencDictAddInt( top, "tag", TAG_PEERS );
1889
tr_bencListAddStr( fields, "peers" );
1890
addIdArg( args, id );
1892
case 942: tr_bencDictAddInt( top, "tag", TAG_PIECES );
1893
tr_bencListAddStr( fields, "pieces" );
1894
tr_bencListAddStr( fields, "pieceCount" );
1895
addIdArg( args, id );
1897
case 943: tr_bencDictAddInt( top, "tag", TAG_TRACKERS );
1898
tr_bencListAddStr( fields, "trackerStats" );
1899
addIdArg( args, id );
1901
default: assert( "unhandled value" && 0 );
1904
status |= flush( host, port, &top );
1906
else if( stepMode == MODE_SESSION_SET )
1908
tr_benc * args = ensure_sset( &sset );
1912
case 800: tr_bencDictAddStr( args, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, optarg );
1913
tr_bencDictAddBool( args, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, TRUE );
1915
case 801: tr_bencDictAddBool( args, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, FALSE );
1917
case 970: tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_ENABLED, TRUE );
1919
case 971: tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_ENABLED, FALSE );
1921
case 972: tr_bencDictAddInt( args, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, numarg( optarg ) );
1923
case 973: tr_bencDictAddInt( args, TR_PREFS_KEY_ALT_SPEED_UP_KBps, numarg( optarg ) );
1925
case 974: tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, TRUE );
1927
case 975: tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, FALSE );
1929
case 976: addTime( args, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, optarg );
1931
case 977: addTime( args, TR_PREFS_KEY_ALT_SPEED_TIME_END, optarg );
1933
case 978: addDays( args, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, optarg );
1935
case 'c': tr_bencDictAddStr( args, TR_PREFS_KEY_INCOMPLETE_DIR, optarg );
1936
tr_bencDictAddBool( args, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, TRUE );
1938
case 'C': tr_bencDictAddBool( args, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, FALSE );
1940
case 'e': tr_bencDictAddInt( args, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, atoi(optarg) );
1942
case 910: tr_bencDictAddStr( args, TR_PREFS_KEY_ENCRYPTION, "required" );
1944
case 911: tr_bencDictAddStr( args, TR_PREFS_KEY_ENCRYPTION, "preferred" );
1946
case 912: tr_bencDictAddStr( args, TR_PREFS_KEY_ENCRYPTION, "tolerated" );
1948
case 'm': tr_bencDictAddBool( args, TR_PREFS_KEY_PORT_FORWARDING, TRUE );
1950
case 'M': tr_bencDictAddBool( args, TR_PREFS_KEY_PORT_FORWARDING, FALSE );
1952
case 'o': tr_bencDictAddBool( args, TR_PREFS_KEY_DHT_ENABLED, TRUE );
1954
case 'O': tr_bencDictAddBool( args, TR_PREFS_KEY_DHT_ENABLED, FALSE );
1956
case 'p': tr_bencDictAddInt( args, TR_PREFS_KEY_PEER_PORT, numarg( optarg ) );
1958
case 'P': tr_bencDictAddBool( args, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, TRUE);
1960
case 'x': tr_bencDictAddBool( args, TR_PREFS_KEY_PEX_ENABLED, TRUE );
1962
case 'X': tr_bencDictAddBool( args, TR_PREFS_KEY_PEX_ENABLED, FALSE );
1964
case 'y': tr_bencDictAddBool( args, TR_PREFS_KEY_LPD_ENABLED, TRUE );
1966
case 'Y': tr_bencDictAddBool( args, TR_PREFS_KEY_LPD_ENABLED, FALSE );
1968
case 953: tr_bencDictAddReal( args, "seedRatioLimit", atof(optarg) );
1969
tr_bencDictAddBool( args, "seedRatioLimited", TRUE );
1971
case 954: tr_bencDictAddBool( args, "seedRatioLimited", FALSE );
1973
case 990: tr_bencDictAddBool( args, TR_PREFS_KEY_START, FALSE );
1975
case 991: tr_bencDictAddBool( args, TR_PREFS_KEY_START, TRUE );
1977
case 992: tr_bencDictAddBool( args, TR_PREFS_KEY_TRASH_ORIGINAL, TRUE );
1979
case 993: tr_bencDictAddBool( args, TR_PREFS_KEY_TRASH_ORIGINAL, FALSE );
1981
default: assert( "unhandled value" && 0 );
1985
else if( stepMode == ( MODE_SESSION_SET | MODE_TORRENT_SET ) )
1987
tr_benc * targs = 0;
1988
tr_benc * sargs = 0;
1991
targs = ensure_tset( &tset );
1993
sargs = ensure_sset( &sset );
1997
case 'd': if( targs ) {
1998
tr_bencDictAddInt( targs, "downloadLimit", numarg( optarg ) );
1999
tr_bencDictAddBool( targs, "downloadLimited", TRUE );
2001
tr_bencDictAddInt( sargs, TR_PREFS_KEY_DSPEED_KBps, numarg( optarg ) );
2002
tr_bencDictAddBool( sargs, TR_PREFS_KEY_DSPEED_ENABLED, TRUE );
2005
case 'D': if( targs )
2006
tr_bencDictAddBool( targs, "downloadLimited", FALSE );
2008
tr_bencDictAddBool( sargs, TR_PREFS_KEY_DSPEED_ENABLED, FALSE );
2010
case 'u': if( targs ) {
2011
tr_bencDictAddInt( targs, "uploadLimit", numarg( optarg ) );
2012
tr_bencDictAddBool( targs, "uploadLimited", TRUE );
2014
tr_bencDictAddInt( sargs, TR_PREFS_KEY_USPEED_KBps, numarg( optarg ) );
2015
tr_bencDictAddBool( sargs, TR_PREFS_KEY_USPEED_ENABLED, TRUE );
2018
case 'U': if( targs )
2019
tr_bencDictAddBool( targs, "uploadLimited", FALSE );
2021
tr_bencDictAddBool( sargs, TR_PREFS_KEY_USPEED_ENABLED, FALSE );
2023
case 930: if( targs )
2024
tr_bencDictAddInt( targs, "peer-limit", atoi(optarg) );
2026
tr_bencDictAddInt( sargs, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, atoi(optarg) );
2028
default: assert( "unhandled value" && 0 );
2032
else if( stepMode == MODE_TORRENT_SET )
2034
tr_benc * args = ensure_tset( &tset );
2038
case 712: tr_bencListAddInt( tr_bencDictAddList( args, "trackerRemove", 1 ), atoi( optarg ) );
2040
case 950: tr_bencDictAddReal( args, "seedRatioLimit", atof(optarg) );
2041
tr_bencDictAddInt( args, "seedRatioMode", TR_RATIOLIMIT_SINGLE );
2043
case 951: tr_bencDictAddInt( args, "seedRatioMode", TR_RATIOLIMIT_GLOBAL );
2045
case 952: tr_bencDictAddInt( args, "seedRatioMode", TR_RATIOLIMIT_UNLIMITED );
2047
case 984: tr_bencDictAddBool( args, "honorsSessionLimits", TRUE );
2049
case 985: tr_bencDictAddBool( args, "honorsSessionLimits", FALSE );
2051
default: assert( "unhandled value" && 0 );
2055
else if( stepMode == ( MODE_TORRENT_SET | MODE_TORRENT_ADD ) )
2060
args = tr_bencDictFind( tadd, ARGUMENTS );
2062
args = ensure_tset( &tset );
2066
case 'g': addFiles( args, "files-wanted", optarg );
2068
case 'G': addFiles( args, "files-unwanted", optarg );
2070
case 900: addFiles( args, "priority-high", optarg );
2072
case 901: addFiles( args, "priority-normal", optarg );
2074
case 902: addFiles( args, "priority-low", optarg );
2076
case 700: tr_bencDictAddInt( args, "bandwidthPriority", 1 );
2078
case 701: tr_bencDictAddInt( args, "bandwidthPriority", 0 );
2080
case 702: tr_bencDictAddInt( args, "bandwidthPriority", -1 );
2082
case 710: tr_bencListAddStr( tr_bencDictAddList( args, "trackerAdd", 1 ), optarg );
2084
default: assert( "unhandled value" && 0 );
2088
else if( c == 961 ) /* set location */
2092
tr_benc * args = tr_bencDictFind( tadd, ARGUMENTS );
2093
tr_bencDictAddStr( args, "download-dir", optarg );
2098
tr_benc * top = tr_new0( tr_benc, 1 );
2099
tr_bencInitDict( top, 2 );
2100
tr_bencDictAddStr( top, "method", "torrent-set-location" );
2101
args = tr_bencDictAddDict( top, ARGUMENTS, 3 );
2102
tr_bencDictAddStr( args, "location", optarg );
2103
tr_bencDictAddBool( args, "move", FALSE );
2104
addIdArg( args, id );
2105
status |= flush( host, port, &top );
2111
case 920: /* session-info */
2113
tr_benc * top = tr_new0( tr_benc, 1 );
2114
tr_bencInitDict( top, 2 );
2115
tr_bencDictAddStr( top, "method", "session-get" );
2116
tr_bencDictAddInt( top, "tag", TAG_SESSION );
2117
status |= flush( host, port, &top );
2120
case 's': /* start */
2123
tr_bencDictAddBool( tr_bencDictFind( tadd, "arguments" ), "paused", FALSE );
2125
tr_benc * top = tr_new0( tr_benc, 1 );
2126
tr_bencInitDict( top, 2 );
2127
tr_bencDictAddStr( top, "method", "torrent-start" );
2128
addIdArg( tr_bencDictAddDict( top, ARGUMENTS, 1 ), id );
2129
status |= flush( host, port, &top );
2133
case 'S': /* stop */
2136
tr_bencDictAddBool( tr_bencDictFind( tadd, "arguments" ), "paused", TRUE );
2138
tr_benc * top = tr_new0( tr_benc, 1 );
2139
tr_bencInitDict( top, 2 );
2140
tr_bencDictAddStr( top, "method", "torrent-stop" );
2141
addIdArg( tr_bencDictAddDict( top, ARGUMENTS, 1 ), id );
2142
status |= flush( host, port, &top );
2148
char * path = absolutify( optarg );
2150
tr_bencDictAddStr( tr_bencDictFind( tadd, "arguments" ), "download-dir", path );
2152
tr_benc * args = ensure_sset( &sset );
2153
tr_bencDictAddStr( args, "download-dir", path );
2160
tr_benc * top = tr_new0( tr_benc, 1 );
2161
tr_bencInitDict( top, 1 );
2162
tr_bencDictAddStr( top, "method", "blocklist-update" );
2163
status |= flush( host, port, &top );
2168
tr_benc * top = tr_new0( tr_benc, 1 );
2169
tr_bencInitDict( top, 2 );
2170
tr_bencDictAddStr( top, "method", "session-stats" );
2171
tr_bencDictAddInt( top, "tag", TAG_STATS );
2172
status |= flush( host, port, &top );
2177
tr_benc * top = tr_new0( tr_benc, 1 );
2178
tr_bencInitDict( top, 2 );
2179
tr_bencDictAddStr( top, "method", "port-test" );
2180
tr_bencDictAddInt( top, "tag", TAG_PORTTEST );
2181
status |= flush( host, port, &top );
2187
if( tset != 0 ) { addIdArg( tr_bencDictFind( tset, ARGUMENTS ), id ); status |= flush( host, port, &tset ); }
2188
top = tr_new0( tr_benc, 1 );
2189
tr_bencInitDict( top, 2 );
2190
tr_bencDictAddStr( top, "method", "torrent-verify" );
2191
addIdArg( tr_bencDictAddDict( top, ARGUMENTS, 1 ), id );
2192
status |= flush( host, port, &top );
2199
tr_benc * top = tr_new0( tr_benc, 1 );
2200
tr_bencInitDict( top, 2 );
2201
tr_bencDictAddStr( top, "method", "torrent-remove" );
2202
args = tr_bencDictAddDict( top, ARGUMENTS, 2 );
2203
tr_bencDictAddBool( args, "delete-local-data", c=='R' );
2204
addIdArg( args, id );
2205
status |= flush( host, port, &top );
2211
tr_benc * top = tr_new0( tr_benc, 1 );
2212
tr_bencInitDict( top, 2 );
2213
tr_bencDictAddStr( top, "method", "torrent-set-location" );
2214
args = tr_bencDictAddDict( top, ARGUMENTS, 3 );
2215
tr_bencDictAddStr( args, "location", optarg );
2216
tr_bencDictAddBool( args, "move", TRUE );
2217
addIdArg( args, id );
2218
status |= flush( host, port, &top );
2223
fprintf( stderr, "got opt [%d]\n", (int)c );
2230
if( tadd != 0 ) status |= flush( host, port, &tadd );
2231
if( tset != 0 ) { addIdArg( tr_bencDictFind( tset, ARGUMENTS ), id ); status |= flush( host, port, &tset ); }
2232
if( sset != 0 ) status |= flush( host, port, &sset );
2236
/* [host:port] or [host] or [port] */
2238
getHostAndPort( int * argc, char ** argv, char ** host, int * port )
2240
if( *argv[1] != '-' )
2243
const char * s = argv[1];
2244
const char * delim = strchr( s, ':' );
2245
if( delim ) /* user passed in both host and port */
2247
*host = tr_strndup( s, delim - s );
2248
*port = atoi( delim + 1 );
2253
const int i = strtol( s, &end, 10 );
2254
if( !*end ) /* user passed in a port */
2256
else /* user passed in a host */
2257
*host = tr_strdup( s );
2261
for( i = 1; i < *argc; ++i )
2262
argv[i] = argv[i + 1];
2267
main( int argc, char ** argv )
2269
int port = DEFAULT_PORT;
2271
int exit_status = EXIT_SUCCESS;
2275
return EXIT_FAILURE;
2278
tr_formatter_mem_init( MEM_K, MEM_K_STR, MEM_M_STR, MEM_G_STR, MEM_T_STR );
2279
tr_formatter_size_init( DISK_K,DISK_K_STR, DISK_M_STR, DISK_G_STR, DISK_T_STR );
2280
tr_formatter_speed_init( SPEED_K, SPEED_K_STR, SPEED_M_STR, SPEED_G_STR, SPEED_T_STR );
2282
getHostAndPort( &argc, argv, &host, &port );
2284
host = tr_strdup( DEFAULT_HOST );
2286
exit_status = processArgs( host, port, argc, (const char**)argv );