~ubuntu-branches/ubuntu/precise/transmission/precise

« back to all changes in this revision

Viewing changes to .pc/0115-fix_tall_argument.patch/daemon/remote.c

  • Committer: Bazaar Package Importer
  • Author(s): Krzysztof Klimonda
  • Date: 2011-03-22 18:59:19 UTC
  • Revision ID: james.westby@ubuntu.com-20110322185919-ceizoba779xccx1r
Tags: 2.13-0ubuntu7
* debian/patches/0108-sync_web_interface_turtle_mode_with_gui.patch
  - Keep GUI and web interface in sync when toggling speed limit mode.
    (LP: #727629)
* debian/patches/0109-fix_crash_on_missing_script.patch (LP: #710003)
  - Fix crash when the configured script is not found.
* debian/patches/0110-dont_truncate_string.patch:
  - Don't truncace string when transmission-edit -r option is used. 
* debian/patches/0111-call_guessPacketOverhead_for_reads.patch
  - Fix peer-io code so packet overhead is calculated for socket reads.
* 0112-fix_check_for_result_of_tr_torrentGetMetadataPercent.patch:
  - tr_torrentGetMetadataPercent() may return NULL; fix the check in
    torrent-magnet.c to handle it.
* debian/patches/0113-fix_support_for_ipv6_trackers.patch:
  - Fix support for IPv6-only trackers.
* debian/patches/0114-dont_move_file_if_src_and_dest_are_the_same.patch:
  - Don't move files and directories if the destination path is the same
    as origin.
* debian/patches/0115-fix_tall_argument.patch:
  - Fix the "-tall" argument in transmission-remote.
* debian/patches/0116-fix_segfault_in_gtk_client.patch:
  - Fix crash in the Gtk+ client when adding many torrents remotely.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * This file Copyright (C) 2008-2010 Mnemosyne LLC
 
3
 *
 
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.
 
7
 *
 
8
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 
9
 *
 
10
 * $Id: remote.c 11318 2010-10-16 16:08:40Z charles $
 
11
 */
 
12
 
 
13
#include <assert.h>
 
14
#include <ctype.h> /* isspace */
 
15
#include <errno.h>
 
16
#include <math.h>
 
17
#include <stdio.h>
 
18
#include <stdlib.h>
 
19
#include <string.h> /* strcmp */
 
20
 
 
21
#ifdef WIN32
 
22
 #include <direct.h> /* getcwd */
 
23
#else
 
24
 #include <unistd.h> /* getcwd */
 
25
#endif
 
26
 
 
27
#include <event.h>
 
28
 
 
29
#define CURL_DISABLE_TYPECHECK /* otherwise -Wunreachable-code goes insane */
 
30
#include <curl/curl.h>
 
31
 
 
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>
 
39
 
 
40
#define MY_NAME "transmission-remote"
 
41
#define DEFAULT_HOST "localhost"
 
42
#define DEFAULT_PORT atoi(TR_DEFAULT_RPC_PORT_STR)
 
43
 
 
44
#define ARGUMENTS "arguments"
 
45
 
 
46
#define MEM_K 1024
 
47
#define MEM_B_STR   "B"
 
48
#define MEM_K_STR "KiB"
 
49
#define MEM_M_STR "MiB"
 
50
#define MEM_G_STR "GiB"
 
51
#define MEM_T_STR "TiB"
 
52
 
 
53
#define DISK_K 1024
 
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"
 
59
 
 
60
#define SPEED_K 1024
 
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"
 
66
 
 
67
/***
 
68
****
 
69
****  Display Utilities
 
70
****
 
71
***/
 
72
 
 
73
static void
 
74
etaToString( char *  buf, size_t  buflen, int64_t eta )
 
75
{
 
76
    if( eta < 0 )
 
77
        tr_snprintf( buf, buflen, "Unknown" );
 
78
    else if( eta < 60 )
 
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 ) );
 
84
    else
 
85
        tr_snprintf( buf, buflen, "%" PRId64 " days", eta / ( 60 * 60 * 24 ) );
 
86
}
 
87
 
 
88
static char*
 
89
tr_strltime( char * buf, int seconds, size_t buflen )
 
90
{
 
91
    int  days, hours, minutes;
 
92
    char d[128], h[128], m[128], s[128];
 
93
 
 
94
    if( seconds < 0 )
 
95
        seconds = 0;
 
96
 
 
97
    days = seconds / 86400;
 
98
    hours = ( seconds % 86400 ) / 3600;
 
99
    minutes = ( seconds % 3600 ) / 60;
 
100
    seconds = ( seconds % 3600 ) % 60;
 
101
 
 
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" );
 
106
 
 
107
    if( days )
 
108
    {
 
109
        if( days >= 4 || !hours )
 
110
            tr_strlcpy( buf, d, buflen );
 
111
        else
 
112
            tr_snprintf( buf, buflen, "%s, %s", d, h );
 
113
    }
 
114
    else if( hours )
 
115
    {
 
116
        if( hours >= 4 || !minutes )
 
117
            tr_strlcpy( buf, h, buflen );
 
118
        else
 
119
            tr_snprintf( buf, buflen, "%s, %s", h, m );
 
120
    }
 
121
    else if( minutes )
 
122
    {
 
123
        if( minutes >= 4 || !seconds )
 
124
            tr_strlcpy( buf, m, buflen );
 
125
        else
 
126
            tr_snprintf( buf, buflen, "%s, %s", m, s );
 
127
    }
 
128
    else tr_strlcpy( buf, s, buflen );
 
129
 
 
130
    return buf;
 
131
}
 
132
 
 
133
static char*
 
134
strlpercent( char * buf, double x, size_t buflen )
 
135
{
 
136
    return tr_strpercent( buf, x, buflen );
 
137
}
 
138
 
 
139
static char*
 
140
strlratio2( char * buf, double ratio, size_t buflen )
 
141
{
 
142
    return tr_strratio( buf, buflen, ratio, "Inf" );
 
143
}
 
144
 
 
145
static char*
 
146
strlratio( char * buf, int64_t numerator, int64_t denominator, size_t buflen )
 
147
{
 
148
    double ratio;
 
149
 
 
150
    if( denominator != 0 )
 
151
        ratio = numerator / (double)denominator;
 
152
    else if( numerator != 0 )
 
153
        ratio = TR_RATIO_INF;
 
154
    else
 
155
        ratio = TR_RATIO_NA;
 
156
 
 
157
    return strlratio2( buf, ratio, buflen );
 
158
}
 
159
 
 
160
static char*
 
161
strlmem( char * buf, int64_t bytes, size_t buflen )
 
162
{
 
163
    if( !bytes )
 
164
        tr_strlcpy( buf, "None", buflen );
 
165
    else
 
166
        tr_formatter_mem_B( buf, bytes, buflen );
 
167
 
 
168
    return buf;
 
169
}
 
170
 
 
171
static char*
 
172
strlsize( char * buf, int64_t bytes, size_t buflen )
 
173
{
 
174
    if( !bytes )
 
175
        tr_strlcpy( buf, "None", buflen );
 
176
    else
 
177
        tr_formatter_size_B( buf, bytes, buflen );
 
178
 
 
179
    return buf;
 
180
}
 
181
 
 
182
enum
 
183
{
 
184
    TAG_SESSION,
 
185
    TAG_STATS,
 
186
    TAG_DETAILS,
 
187
    TAG_FILES,
 
188
    TAG_LIST,
 
189
    TAG_PEERS,
 
190
    TAG_PIECES,
 
191
    TAG_PORTTEST,
 
192
    TAG_TORRENT_ADD,
 
193
    TAG_TRACKERS
 
194
};
 
195
 
 
196
static const char*
 
197
getUsage( void )
 
198
{
 
199
    return
 
200
        MY_NAME" "LONG_VERSION_STRING"\n"
 
201
        "A fast and easy BitTorrent client\n"
 
202
        "http://www.transmissionbt.com/\n"
 
203
        "\n"
 
204
        "Usage: " MY_NAME
 
205
        " [host] [options]\n"
 
206
        "       "
 
207
        MY_NAME " [port] [options]\n"
 
208
                "       "
 
209
        MY_NAME " [host:port] [options]\n"
 
210
                "\n"
 
211
                "See the man page for detailed explanations and many examples.";
 
212
}
 
213
 
 
214
/***
 
215
****
 
216
****  Command-Line Arguments
 
217
****
 
218
***/
 
219
 
 
220
static tr_option opts[] =
 
221
{
 
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 }
 
302
};
 
303
 
 
304
static void
 
305
showUsage( void )
 
306
{
 
307
    tr_getopt_usage( MY_NAME, getUsage( ), opts );
 
308
}
 
309
 
 
310
static int
 
311
numarg( const char * arg )
 
312
{
 
313
    char *     end = NULL;
 
314
    const long num = strtol( arg, &end, 10 );
 
315
 
 
316
    if( *end )
 
317
    {
 
318
        fprintf( stderr, "Not a number: \"%s\"\n", arg );
 
319
        showUsage( );
 
320
        exit( EXIT_FAILURE );
 
321
    }
 
322
    return num;
 
323
}
 
324
 
 
325
enum
 
326
{
 
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)
 
341
};
 
342
 
 
343
static int
 
344
getOptMode( int val )
 
345
{
 
346
    switch( val )
 
347
    {
 
348
        case TR_OPT_ERR:
 
349
        case TR_OPT_UNK:
 
350
        case 'a': /* add torrent */
 
351
        case 'b': /* debug */
 
352
        case 'n': /* auth */
 
353
        case 'N': /* netrc */
 
354
        case 't': /* set current torrent */
 
355
        case 'V': /* show version number */
 
356
            return 0;
 
357
 
 
358
        case 'c': /* incomplete-dir */
 
359
        case 'C': /* no-incomplete-dir */
 
360
        case 'e': /* cache */
 
361
        case 'm': /* portmap */
 
362
        case 'M': /* "no-portmap */
 
363
        case 'o': /* dht */
 
364
        case 'O': /* no-dht */
 
365
        case 'p': /* incoming peer port */
 
366
        case 'P': /* random incoming peer port */
 
367
        case 'x': /* pex */
 
368
        case 'X': /* no-pex */
 
369
        case 'y': /* lpd */
 
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;
 
392
 
 
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;
 
400
 
 
401
        case 920: /* session-info */
 
402
            return MODE_SESSION_GET;
 
403
 
 
404
        case 'g': /* 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;
 
414
 
 
415
        case 961: /* find */
 
416
            return MODE_TORRENT_SET_LOCATION | MODE_TORRENT_ADD;
 
417
 
 
418
        case 'i': /* info */
 
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;
 
425
 
 
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;
 
432
 
 
433
        case 's': /* start */
 
434
            return MODE_TORRENT_START | MODE_TORRENT_ADD;
 
435
 
 
436
        case 'S': /* stop */
 
437
            return MODE_TORRENT_STOP | MODE_TORRENT_ADD;
 
438
 
 
439
        case 'w': /* download-dir */
 
440
            return MODE_SESSION_SET | MODE_TORRENT_ADD;
 
441
 
 
442
        case 963: /* blocklist-update */
 
443
            return MODE_BLOCKLIST_UPDATE;
 
444
 
 
445
        case 921: /* session-stats */
 
446
            return MODE_SESSION_STATS;
 
447
 
 
448
        case 'v': /* verify */
 
449
            return MODE_TORRENT_VERIFY;
 
450
 
 
451
        case 962: /* port-test */
 
452
            return MODE_PORT_TEST;
 
453
 
 
454
        case 'r': /* remove */
 
455
        case 'R': /* remove and delete */
 
456
            return MODE_TORRENT_REMOVE;
 
457
 
 
458
        case 960: /* move */
 
459
            return MODE_TORRENT_SET_LOCATION;
 
460
 
 
461
        default:
 
462
            fprintf( stderr, "unrecognized argument %d\n", val );
 
463
            assert( "unrecognized argument" && 0 );
 
464
            return 0;
 
465
    }
 
466
}
 
467
 
 
468
static tr_bool debug = 0;
 
469
static char * auth = NULL;
 
470
static char * netrc = NULL;
 
471
static char * sessionId = NULL;
 
472
 
 
473
static char*
 
474
tr_getcwd( void )
 
475
{
 
476
    char buf[2048];
 
477
    *buf = '\0';
 
478
#ifdef WIN32
 
479
    _getcwd( buf, sizeof( buf ) );
 
480
#else
 
481
    getcwd( buf, sizeof( buf ) );
 
482
#endif
 
483
    return tr_strdup( buf );
 
484
}
 
485
 
 
486
static char*
 
487
absolutify( const char * path )
 
488
{
 
489
    char * buf;
 
490
 
 
491
    if( *path == '/' )
 
492
        buf = tr_strdup( path );
 
493
    else {
 
494
        char * cwd = tr_getcwd( );
 
495
        buf = tr_buildPath( cwd, path, NULL );
 
496
        tr_free( cwd );
 
497
    }
 
498
 
 
499
    return buf;
 
500
}
 
501
 
 
502
static char*
 
503
getEncodedMetainfo( const char * filename )
 
504
{
 
505
    size_t    len = 0;
 
506
    char *    b64 = NULL;
 
507
    uint8_t * buf = tr_loadFile( filename, &len );
 
508
 
 
509
    if( buf )
 
510
    {
 
511
        b64 = tr_base64_encode( buf, len, NULL );
 
512
        tr_free( buf );
 
513
    }
 
514
    return b64;
 
515
}
 
516
 
 
517
static void
 
518
addIdArg( tr_benc * args, const char * id )
 
519
{
 
520
    if( !*id )
 
521
    {
 
522
        fprintf(
 
523
            stderr,
 
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 */
 
526
    }
 
527
    if( strcmp( id, "all" ) )
 
528
    {
 
529
        const char * pch;
 
530
        tr_bool isList = strchr(id,',') || strchr(id,'-');
 
531
        tr_bool isNum = TRUE;
 
532
        for( pch=id; isNum && *pch; ++pch )
 
533
            if( !isdigit( *pch ) )
 
534
                isNum = FALSE;
 
535
        if( isNum || isList )
 
536
            tr_rpc_parse_list_str( tr_bencDictAdd( args, "ids" ), id, strlen( id ) );
 
537
        else
 
538
            tr_bencDictAddStr( args, "ids", id ); /* it's a torrent sha hash */
 
539
    }
 
540
}
 
541
 
 
542
static void
 
543
addTime( tr_benc * args, const char * key, const char * arg )
 
544
{
 
545
    int time;
 
546
    tr_bool success = FALSE;
 
547
 
 
548
    if( arg && ( strlen( arg ) == 4 ) )
 
549
    {
 
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 );
 
554
 
 
555
        if( 0<=hour && hour<24 && 0<=min && min<60 )
 
556
        {
 
557
            time = min + ( hour * 60 );
 
558
            success = TRUE;
 
559
        }
 
560
    }
 
561
 
 
562
    if( success )
 
563
        tr_bencDictAddInt( args, key, time );
 
564
    else
 
565
        fprintf( stderr, "Please specify the time of day in 'hhmm' format.\n" );
 
566
}
 
567
 
 
568
static void
 
569
addDays( tr_benc * args, const char * key, const char * arg )
 
570
{
 
571
    int days = 0;
 
572
 
 
573
    if( arg )
 
574
    {
 
575
        int i;
 
576
        int valueCount;
 
577
        int * values = tr_parseNumberRange( arg, -1, &valueCount );
 
578
        for( i=0; i<valueCount; ++i )
 
579
        {
 
580
            if ( values[i] < 0 || values[i] > 7 ) continue;
 
581
            if ( values[i] == 7 ) values[i] = 0;
 
582
 
 
583
            days |= 1 << values[i];
 
584
        }
 
585
        tr_free( values );
 
586
    }
 
587
 
 
588
    if ( days )
 
589
        tr_bencDictAddInt( args, key, days );
 
590
    else
 
591
        fprintf( stderr, "Please specify the days of the week in '1-3,4,7' format.\n" );
 
592
}
 
593
 
 
594
static void
 
595
addFiles( tr_benc *    args,
 
596
          const char * key,
 
597
          const char * arg )
 
598
{
 
599
    tr_benc * files = tr_bencDictAddList( args, key, 100 );
 
600
 
 
601
    if( !*arg )
 
602
    {
 
603
        fprintf( stderr, "No files specified!\n" );
 
604
        arg = "-1"; /* no file will have this index, so should be a no-op */
 
605
    }
 
606
    if( strcmp( arg, "all" ) )
 
607
    {
 
608
        int i;
 
609
        int valueCount;
 
610
        int * values = tr_parseNumberRange( arg, -1, &valueCount );
 
611
        for( i=0; i<valueCount; ++i )
 
612
            tr_bencListAddInt( files, values[i] );
 
613
        tr_free( values );
 
614
    }
 
615
}
 
616
 
 
617
#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) )
 
618
 
 
619
static const char * files_keys[] = {
 
620
    "files",
 
621
    "name",
 
622
    "priorities",
 
623
    "wanted"
 
624
};
 
625
 
 
626
static const char * details_keys[] = {
 
627
    "activityDate",
 
628
    "addedDate",
 
629
    "bandwidthPriority",
 
630
    "comment",
 
631
    "corruptEver",
 
632
    "creator",
 
633
    "dateCreated",
 
634
    "desiredAvailable",
 
635
    "doneDate",
 
636
    "downloadDir",
 
637
    "downloadedEver",
 
638
    "downloadLimit",
 
639
    "downloadLimited",
 
640
    "error",
 
641
    "errorString",
 
642
    "eta",
 
643
    "hashString",
 
644
    "haveUnchecked",
 
645
    "haveValid",
 
646
    "honorsSessionLimits",
 
647
    "id",
 
648
    "isFinished",
 
649
    "isPrivate",
 
650
    "leftUntilDone",
 
651
    "name",
 
652
    "peersConnected",
 
653
    "peersGettingFromUs",
 
654
    "peersSendingToUs",
 
655
    "peer-limit",
 
656
    "pieceCount",
 
657
    "pieceSize",
 
658
    "rateDownload",
 
659
    "rateUpload",
 
660
    "recheckProgress",
 
661
    "seedRatioMode",
 
662
    "seedRatioLimit",
 
663
    "sizeWhenDone",
 
664
    "startDate",
 
665
    "status",
 
666
    "totalSize",
 
667
    "uploadedEver",
 
668
    "uploadLimit",
 
669
    "uploadLimited",
 
670
    "webseeds",
 
671
    "webseedsSendingToUs"
 
672
};
 
673
 
 
674
static const char * list_keys[] = {
 
675
    "error",
 
676
    "errorString",
 
677
    "eta",
 
678
    "id",
 
679
    "isFinished",
 
680
    "leftUntilDone",
 
681
    "name",
 
682
    "peersGettingFromUs",
 
683
    "peersSendingToUs",
 
684
    "rateDownload",
 
685
    "rateUpload",
 
686
    "sizeWhenDone",
 
687
    "status",
 
688
    "uploadRatio"
 
689
};
 
690
 
 
691
static size_t
 
692
writeFunc( void * ptr, size_t size, size_t nmemb, void * buf )
 
693
{
 
694
    const size_t byteCount = size * nmemb;
 
695
    evbuffer_add( buf, ptr, byteCount );
 
696
    return byteCount;
 
697
}
 
698
 
 
699
/* look for a session id in the header in case the server gives back a 409 */
 
700
static size_t
 
701
parseResponseHeader( void *ptr, size_t size, size_t nmemb, void * stream UNUSED )
 
702
{
 
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 );
 
707
 
 
708
    if( ( line_len >= key_len ) && !memcmp( line, key, key_len ) )
 
709
    {
 
710
        const char * begin = line + key_len;
 
711
        const char * end = begin;
 
712
        while( !isspace( *end ) )
 
713
            ++end;
 
714
        tr_free( sessionId );
 
715
        sessionId = tr_strndup( begin, end-begin );
 
716
    }
 
717
 
 
718
    return line_len;
 
719
}
 
720
 
 
721
static long
 
722
getTimeoutSecs( const char * req )
 
723
{
 
724
  if( strstr( req, "\"method\":\"blocklist-update\"" ) != NULL )
 
725
    return 300L;
 
726
 
 
727
  return 60L; /* default value */
 
728
}
 
729
 
 
730
static char*
 
731
getStatusString( tr_benc * t, char * buf, size_t buflen )
 
732
{
 
733
    int64_t status;
 
734
    tr_bool boolVal;
 
735
 
 
736
    if( !tr_bencDictFindInt( t, "status", &status ) )
 
737
    {
 
738
        *buf = '\0';
 
739
    }
 
740
    else switch( status )
 
741
    {
 
742
        case TR_STATUS_STOPPED:
 
743
            if( tr_bencDictFindBool( t, "isFinished", &boolVal ) && boolVal )
 
744
                tr_strlcpy( buf, "Finished", buflen );
 
745
            else
 
746
                tr_strlcpy( buf, "Stopped", buflen );
 
747
            break;
 
748
 
 
749
        case TR_STATUS_CHECK_WAIT:
 
750
        case TR_STATUS_CHECK: {
 
751
            const char * str = status == TR_STATUS_CHECK_WAIT
 
752
                             ? "Will Verify"
 
753
                             : "Verifying";
 
754
            double percent;
 
755
            if( tr_bencDictFindReal( t, "recheckProgress", &percent ) )
 
756
                tr_snprintf( buf, buflen, "%s (%.0f%%)", str, floor(percent*100.0) );
 
757
            else
 
758
                tr_strlcpy( buf, str, buflen );
 
759
 
 
760
            break;
 
761
        }
 
762
 
 
763
        case TR_STATUS_DOWNLOAD:
 
764
        case TR_STATUS_SEED: {
 
765
            int64_t fromUs = 0;
 
766
            int64_t toUs = 0;
 
767
            tr_bencDictFindInt( t, "peersGettingFromUs", &fromUs );
 
768
            tr_bencDictFindInt( t, "peersSendingToUs", &toUs );
 
769
            if( fromUs && toUs )
 
770
                tr_strlcpy( buf, "Up & Down", buflen );
 
771
            else if( toUs )
 
772
                tr_strlcpy( buf, "Downloading", buflen );
 
773
            else if( fromUs ) {
 
774
                int64_t leftUntilDone = 0;
 
775
                tr_bencDictFindInt( t, "leftUntilDone", &leftUntilDone );
 
776
                if( leftUntilDone > 0 )
 
777
                    tr_strlcpy( buf, "Uploading", buflen );
 
778
                else
 
779
                    tr_strlcpy( buf, "Seeding", buflen );
 
780
            } else {
 
781
                tr_strlcpy( buf, "Idle", buflen );
 
782
            }
 
783
            break;
 
784
        }
 
785
    }
 
786
 
 
787
    return buf;
 
788
}
 
789
 
 
790
static const char *bandwidthPriorityNames[] =
 
791
    { "Low", "Normal", "High", "Invalid" };
 
792
 
 
793
static void
 
794
printDetails( tr_benc * top )
 
795
{
 
796
    tr_benc *args, *torrents;
 
797
 
 
798
    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
 
799
      && ( tr_bencDictFindList( args, "torrents", &torrents ) ) )
 
800
    {
 
801
        int ti, tCount;
 
802
        for( ti = 0, tCount = tr_bencListSize( torrents ); ti < tCount;
 
803
             ++ti )
 
804
        {
 
805
            tr_benc *    t = tr_bencListChild( torrents, ti );
 
806
            tr_benc *    l;
 
807
            const char * str;
 
808
            char         buf[512];
 
809
            char         buf2[512];
 
810
            int64_t      i, j, k;
 
811
            tr_bool      boolVal;
 
812
            double       d;
 
813
 
 
814
            printf( "NAME\n" );
 
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 );
 
821
            printf( "\n" );
 
822
 
 
823
            printf( "TRANSFER\n" );
 
824
            getStatusString( t, buf, sizeof( buf ) );
 
825
            printf( "  State: %s\n", buf );
 
826
 
 
827
            if( tr_bencDictFindStr( t, "downloadDir", &str ) )
 
828
                printf( "  Location: %s\n", str );
 
829
 
 
830
            if( tr_bencDictFindInt( t, "sizeWhenDone", &i )
 
831
              && tr_bencDictFindInt( t, "leftUntilDone", &j ) )
 
832
            {
 
833
                strlpercent( buf, 100.0 * ( i - j ) / i, sizeof( buf ) );
 
834
                printf( "  Percent Done: %s%%\n", buf );
 
835
            }
 
836
 
 
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 ) )
 
845
            {
 
846
                strlsize( buf, i + j, sizeof( buf ) );
 
847
                strlsize( buf2, j, sizeof( buf2 ) );
 
848
                printf( "  Have: %s (%s verified)\n", buf, buf2 );
 
849
            }
 
850
 
 
851
            if( tr_bencDictFindInt( t, "sizeWhenDone", &i ) )
 
852
            {
 
853
                if( i < 1 )
 
854
                    printf( "  Availability: None\n" );
 
855
                if( tr_bencDictFindInt( t, "desiredAvailable", &j)
 
856
                    && tr_bencDictFindInt( t, "leftUntilDone", &k) )
 
857
                {
 
858
                    j += i - k;
 
859
                    strlpercent( buf, 100.0 * j / i, sizeof( buf ) );
 
860
                    printf( "  Availability: %s%%\n", buf );
 
861
                }
 
862
                if( tr_bencDictFindInt( t, "totalSize", &j ) )
 
863
                {
 
864
                    strlsize( buf2, i, sizeof( buf2 ) );
 
865
                    strlsize( buf, j, sizeof( buf ) );
 
866
                    printf( "  Total size: %s (%s wanted)\n", buf, buf2 );
 
867
                }
 
868
            }
 
869
            if( tr_bencDictFindInt( t, "downloadedEver", &i )
 
870
              && tr_bencDictFindInt( t, "uploadedEver", &j ) )
 
871
            {
 
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 );
 
878
            }
 
879
            if( tr_bencDictFindInt( t, "seedRatioMode", &i))
 
880
            {
 
881
                switch( i ) {
 
882
                    case TR_RATIOLIMIT_GLOBAL:
 
883
                        printf( "  Ratio Limit: Default\n" );
 
884
                        break;
 
885
                    case TR_RATIOLIMIT_SINGLE:
 
886
                        if( tr_bencDictFindReal( t, "seedRatioLimit", &d))
 
887
                            printf( "  Ratio Limit: %.2f\n", d );
 
888
                        break;
 
889
                    case TR_RATIOLIMIT_UNLIMITED:
 
890
                        printf( "  Ratio Limit: Unlimited\n" );
 
891
                        break;
 
892
                    default: break;
 
893
                }
 
894
            }
 
895
            if( tr_bencDictFindInt( t, "corruptEver", &i ) )
 
896
            {
 
897
                strlsize( buf, i, sizeof( buf ) );
 
898
                printf( "  Corrupt DL: %s\n", buf );
 
899
            }
 
900
            if( tr_bencDictFindStr( t, "errorString", &str ) && str && *str &&
 
901
                tr_bencDictFindInt( t, "error", &i ) && i )
 
902
            {
 
903
                switch( 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 */
 
908
                }
 
909
            }
 
910
            if( tr_bencDictFindInt( t, "peersConnected", &i )
 
911
              && tr_bencDictFindInt( t, "peersGettingFromUs", &j )
 
912
              && tr_bencDictFindInt( t, "peersSendingToUs", &k ) )
 
913
            {
 
914
                printf(
 
915
                    "  Peers: "
 
916
                    "connected to %" PRId64 ", "
 
917
                                            "uploading to %" PRId64
 
918
                    ", "
 
919
                    "downloading from %"
 
920
                    PRId64 "\n",
 
921
                    i, j, k );
 
922
            }
 
923
 
 
924
            if( tr_bencDictFindList( t, "webseeds", &l )
 
925
              && tr_bencDictFindInt( t, "webseedsSendingToUs", &i ) )
 
926
            {
 
927
                const int64_t n = tr_bencListSize( l );
 
928
                if( n > 0 )
 
929
                    printf(
 
930
                        "  Web Seeds: downloading from %" PRId64 " of %"
 
931
                        PRId64
 
932
                        " web seeds\n", i, n );
 
933
            }
 
934
            printf( "\n" );
 
935
 
 
936
            printf( "HISTORY\n" );
 
937
            if( tr_bencDictFindInt( t, "addedDate", &i ) && i )
 
938
            {
 
939
                const time_t tt = i;
 
940
                printf( "  Date added:      %s", ctime( &tt ) );
 
941
            }
 
942
            if( tr_bencDictFindInt( t, "doneDate", &i ) && i )
 
943
            {
 
944
                const time_t tt = i;
 
945
                printf( "  Date finished:   %s", ctime( &tt ) );
 
946
            }
 
947
            if( tr_bencDictFindInt( t, "startDate", &i ) && i )
 
948
            {
 
949
                const time_t tt = i;
 
950
                printf( "  Date started:    %s", ctime( &tt ) );
 
951
            }
 
952
            if( tr_bencDictFindInt( t, "activityDate", &i ) && i )
 
953
            {
 
954
                const time_t tt = i;
 
955
                printf( "  Latest activity: %s", ctime( &tt ) );
 
956
            }
 
957
            printf( "\n" );
 
958
 
 
959
            printf( "ORIGINS\n" );
 
960
            if( tr_bencDictFindInt( t, "dateCreated", &i ) && i )
 
961
            {
 
962
                const time_t tt = i;
 
963
                printf( "  Date created: %s", ctime( &tt ) );
 
964
            }
 
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 ) ) );
 
975
            printf( "\n" );
 
976
 
 
977
            printf( "LIMITS & BANDWIDTH\n" );
 
978
            if( tr_bencDictFindBool( t, "downloadLimited", &boolVal )
 
979
                && tr_bencDictFindInt( t, "downloadLimit", &i ) )
 
980
            {
 
981
                printf( "  Download Limit: " );
 
982
                if( boolVal )
 
983
                    printf( "%s\n", tr_formatter_speed_KBps( buf, i, sizeof( buf ) ) );
 
984
                else
 
985
                    printf( "Unlimited\n" );
 
986
            }
 
987
            if( tr_bencDictFindBool( t, "uploadLimited", &boolVal )
 
988
                && tr_bencDictFindInt( t, "uploadLimit", &i ) )
 
989
            {
 
990
                printf( "  Upload Limit: " );
 
991
                if( boolVal )
 
992
                    printf( "%s\n", tr_formatter_speed_KBps( buf, i, sizeof( buf ) ) );
 
993
                else
 
994
                    printf( "Unlimited\n" );
 
995
            }
 
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]);
 
1003
   
 
1004
            printf( "\n" );
 
1005
        }
 
1006
    }
 
1007
}
 
1008
 
 
1009
static void
 
1010
printFileList( tr_benc * top )
 
1011
{
 
1012
    tr_benc *args, *torrents;
 
1013
 
 
1014
    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
 
1015
      && ( tr_bencDictFindList( args, "torrents", &torrents ) ) )
 
1016
    {
 
1017
        int i, in;
 
1018
        for( i = 0, in = tr_bencListSize( torrents ); i < in; ++i )
 
1019
        {
 
1020
            tr_benc *    d = tr_bencListChild( torrents, i );
 
1021
            tr_benc *    files, *priorities, *wanteds;
 
1022
            const char * name;
 
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 ) )
 
1027
            {
 
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",
 
1032
                        "Name" );
 
1033
                for( j = 0, jn = tr_bencListSize( files ); j < jn; ++j )
 
1034
                {
 
1035
                    int64_t      have;
 
1036
                    int64_t      length;
 
1037
                    int64_t      priority;
 
1038
                    int64_t      wanted;
 
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,
 
1045
                                                          j ), &priority )
 
1046
                      && tr_bencGetInt( tr_bencListChild( wanteds,
 
1047
                                                          j ), &wanted ) )
 
1048
                    {
 
1049
                        char         sizestr[64];
 
1050
                        double       percent = (double)have / length;
 
1051
                        const char * pristr;
 
1052
                        strlsize( sizestr, length, sizeof( sizestr ) );
 
1053
                        switch( priority )
 
1054
                        {
 
1055
                            case TR_PRI_LOW:
 
1056
                                pristr = "Low"; break;
 
1057
 
 
1058
                            case TR_PRI_HIGH:
 
1059
                                pristr = "High"; break;
 
1060
 
 
1061
                            default:
 
1062
                                pristr = "Normal"; break;
 
1063
                        }
 
1064
                        printf( "%3d: %3.0f%% %-8s %-3s %9s  %s\n",
 
1065
                                j,
 
1066
                                floor( 100.0 * percent ),
 
1067
                                pristr,
 
1068
                                ( wanted ? "Yes" : "No" ),
 
1069
                                sizestr,
 
1070
                                filename );
 
1071
                    }
 
1072
                }
 
1073
            }
 
1074
        }
 
1075
    }
 
1076
}
 
1077
 
 
1078
static void
 
1079
printPeersImpl( tr_benc * peers )
 
1080
{
 
1081
    int i, n;
 
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 )
 
1085
    {
 
1086
        double progress;
 
1087
        const char * address, * client, * flagstr;
 
1088
        int64_t rateToClient, rateToPeer;
 
1089
        tr_benc * d = tr_bencListChild( peers, i );
 
1090
 
 
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 ) )
 
1097
        {
 
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,
 
1102
                    client );
 
1103
        }
 
1104
    }
 
1105
}
 
1106
 
 
1107
static void
 
1108
printPeers( tr_benc * top )
 
1109
{
 
1110
    tr_benc *args, *torrents;
 
1111
 
 
1112
    if( tr_bencDictFindDict( top, "arguments", &args )
 
1113
      && tr_bencDictFindList( args, "torrents", &torrents ) )
 
1114
    {
 
1115
        int i, n;
 
1116
        for( i=0, n=tr_bencListSize( torrents ); i<n; ++i )
 
1117
        {
 
1118
            tr_benc * peers;
 
1119
            tr_benc * torrent = tr_bencListChild( torrents, i );
 
1120
            if( tr_bencDictFindList( torrent, "peers", &peers ) ) {
 
1121
                printPeersImpl( peers );
 
1122
                if( i+1<n )
 
1123
                    printf( "\n" );
 
1124
            }
 
1125
        }
 
1126
    }
 
1127
}
 
1128
 
 
1129
static void
 
1130
printPiecesImpl( const uint8_t * raw, size_t rawlen, int64_t j )
 
1131
{
 
1132
    int i, k, len;
 
1133
    char * str = tr_base64_decode( raw, rawlen, &len );
 
1134
    printf( "  " );
 
1135
    for( i=k=0; k<len; ++k ) {
 
1136
        int e;
 
1137
        for( e=0; i<j && e<8; ++e, ++i )
 
1138
            printf( "%c", str[k] & (1<<(7-e)) ? '1' : '0' );
 
1139
        printf( " " );
 
1140
        if( !(i%64) )
 
1141
            printf( "\n  " );
 
1142
    }
 
1143
    printf( "\n" );
 
1144
    tr_free( str );
 
1145
}
 
1146
 
 
1147
static void
 
1148
printPieces( tr_benc * top )
 
1149
{
 
1150
    tr_benc *args, *torrents;
 
1151
 
 
1152
    if( tr_bencDictFindDict( top, "arguments", &args )
 
1153
      && tr_bencDictFindList( args, "torrents", &torrents ) )
 
1154
    {
 
1155
        int i, n;
 
1156
        for( i=0, n=tr_bencListSize( torrents ); i<n; ++i )
 
1157
        {
 
1158
            int64_t j;
 
1159
            const uint8_t * raw;
 
1160
            size_t       rawlen;
 
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 );
 
1165
                if( i+1<n )
 
1166
                    printf( "\n" );
 
1167
            }
 
1168
        }
 
1169
    }
 
1170
}
 
1171
 
 
1172
static void
 
1173
printPortTest( tr_benc * top )
 
1174
{
 
1175
    tr_benc *args;
 
1176
    if( ( tr_bencDictFindDict( top, "arguments", &args ) ) )
 
1177
    {
 
1178
        tr_bool      boolVal;
 
1179
 
 
1180
        if( tr_bencDictFindBool( args, "port-is-open", &boolVal ) )
 
1181
            printf( "Port is open: %s\n", ( boolVal ? "Yes" : "No" ) );
 
1182
    }
 
1183
}
 
1184
 
 
1185
static void
 
1186
printTorrentList( tr_benc * top )
 
1187
{
 
1188
    tr_benc *args, *list;
 
1189
 
 
1190
    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
 
1191
      && ( tr_bencDictFindList( args, "torrents", &list ) ) )
 
1192
    {
 
1193
        int i, n;
 
1194
        int64_t total_size=0;
 
1195
        double total_up=0, total_down=0;
 
1196
        char haveStr[32];
 
1197
 
 
1198
        printf( "%-4s   %-4s  %9s  %-8s  %6s  %6s  %-5s  %-11s  %s\n",
 
1199
                "ID", "Done", "Have", "ETA", "Up", "Down", "Ratio", "Status",
 
1200
                "Name" );
 
1201
 
 
1202
        for( i = 0, n = tr_bencListSize( list ); i < n; ++i )
 
1203
        {
 
1204
            int64_t      id, eta, status, up, down;
 
1205
            int64_t      sizeWhenDone, leftUntilDone;
 
1206
            double       ratio;
 
1207
            const char * name;
 
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 ) )
 
1218
            {
 
1219
                char etaStr[16];
 
1220
                char statusStr[64];
 
1221
                char ratioStr[32];
 
1222
                char doneStr[8];
 
1223
                int64_t error;
 
1224
                char errorMark;
 
1225
 
 
1226
                if( sizeWhenDone )
 
1227
                    tr_snprintf( doneStr, sizeof( doneStr ), "%d%%", (int)( 100.0 * ( sizeWhenDone - leftUntilDone ) / sizeWhenDone ) );
 
1228
                else
 
1229
                    tr_strlcpy( doneStr, "n/a", sizeof( doneStr ) );
 
1230
 
 
1231
                strlsize( haveStr, sizeWhenDone - leftUntilDone, sizeof( haveStr ) );
 
1232
 
 
1233
                if( leftUntilDone || eta != -1 )
 
1234
                    etaToString( etaStr, sizeof( etaStr ), eta );
 
1235
                else
 
1236
                    tr_snprintf( etaStr, sizeof( etaStr ), "Done" );
 
1237
                if( tr_bencDictFindInt( d, "error", &error ) && error )
 
1238
                    errorMark = '*';
 
1239
                else
 
1240
                    errorMark = ' ';
 
1241
                printf(
 
1242
                    "%4d%c  %4s  %9s  %-8s  %6.1f  %6.1f  %5s  %-11s  %s\n",
 
1243
                    (int)id, errorMark,
 
1244
                    doneStr,
 
1245
                    haveStr,
 
1246
                    etaStr,
 
1247
                    up/(double)tr_speed_K,
 
1248
                    down/(double)tr_speed_K,
 
1249
                    strlratio2( ratioStr, ratio, sizeof( ratioStr ) ),
 
1250
                    getStatusString( d, statusStr, sizeof( statusStr ) ),
 
1251
                    name );
 
1252
 
 
1253
                total_up += up;
 
1254
                total_down += down;
 
1255
                total_size += sizeWhenDone - leftUntilDone;
 
1256
            }
 
1257
        }
 
1258
 
 
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 );
 
1263
    }
 
1264
}
 
1265
 
 
1266
static void
 
1267
printTrackersImpl( tr_benc * trackerStats )
 
1268
{
 
1269
    int i;
 
1270
    char         buf[512];
 
1271
    tr_benc * t;
 
1272
 
 
1273
    for( i=0; (( t = tr_bencListChild( trackerStats, i ))); ++i )
 
1274
    {
 
1275
        int64_t downloadCount;
 
1276
        tr_bool hasAnnounced;
 
1277
        tr_bool hasScraped;
 
1278
        const char * host;
 
1279
        int64_t id;
 
1280
        tr_bool isBackup;
 
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;
 
1296
        int64_t tier;
 
1297
        int64_t announceState;
 
1298
        int64_t scrapeState;
 
1299
 
 
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 ) )
 
1324
        {
 
1325
            const time_t now = time( NULL );
 
1326
 
 
1327
            printf( "\n" );
 
1328
            printf( "  Tracker %d: %s\n", (int)(id), host );
 
1329
            if( isBackup )
 
1330
                printf( "  Backup on tier %d\n", (int)tier );
 
1331
            else
 
1332
                printf( "  Active in tier %d\n", (int)tier );
 
1333
 
 
1334
            if( !isBackup )
 
1335
            {
 
1336
                if( hasAnnounced && announceState != TR_TRACKER_INACTIVE )
 
1337
                {
 
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" );
 
1344
                    else
 
1345
                        printf( "  Got an error \"%s\" %s ago\n",
 
1346
                                lastAnnounceResult, buf );
 
1347
                }
 
1348
 
 
1349
                switch( announceState )
 
1350
                {
 
1351
                    case TR_TRACKER_INACTIVE:
 
1352
                        printf( "  No updates scheduled\n" );
 
1353
                        break;
 
1354
                    case TR_TRACKER_WAITING:
 
1355
                        tr_strltime( buf, nextAnnounceTime - now, sizeof( buf ) );
 
1356
                        printf( "  Asking for more peers in %s\n", buf );
 
1357
                        break;
 
1358
                    case TR_TRACKER_QUEUED:
 
1359
                        printf( "  Queued to ask for more peers\n" );
 
1360
                        break;
 
1361
                    case TR_TRACKER_ACTIVE:
 
1362
                        tr_strltime( buf, now - lastAnnounceStartTime, sizeof( buf ) );
 
1363
                        printf( "  Asking for more peers now... %s\n", buf );
 
1364
                        break;
 
1365
                }
 
1366
 
 
1367
                if( hasScraped )
 
1368
                {
 
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" );
 
1375
                    else
 
1376
                        printf( "  Got a scrape error \"%s\" %s ago\n",
 
1377
                                lastScrapeResult, buf );
 
1378
                }
 
1379
 
 
1380
                switch( scrapeState )
 
1381
                {
 
1382
                    case TR_TRACKER_INACTIVE:
 
1383
                        break;
 
1384
                    case TR_TRACKER_WAITING:
 
1385
                        tr_strltime( buf, nextScrapeTime - now, sizeof( buf ) );
 
1386
                        printf( "  Asking for peer counts in %s\n", buf );
 
1387
                        break;
 
1388
                    case TR_TRACKER_QUEUED:
 
1389
                        printf( "  Queued to ask for peer counts\n" );
 
1390
                        break;
 
1391
                    case TR_TRACKER_ACTIVE:
 
1392
                        tr_strltime( buf, now - lastScrapeStartTime, sizeof( buf ) );
 
1393
                        printf( "  Asking for peer counts now... %s\n", buf );
 
1394
                        break;
 
1395
                }
 
1396
            }
 
1397
        }
 
1398
    }
 
1399
}
 
1400
 
 
1401
static void
 
1402
printTrackers( tr_benc * top )
 
1403
{
 
1404
    tr_benc *args, *torrents;
 
1405
 
 
1406
    if( tr_bencDictFindDict( top, "arguments", &args )
 
1407
      && tr_bencDictFindList( args, "torrents", &torrents ) )
 
1408
    {
 
1409
        int i, n;
 
1410
        for( i=0, n=tr_bencListSize( torrents ); i<n; ++i )
 
1411
        {
 
1412
            tr_benc * trackerStats;
 
1413
            tr_benc * torrent = tr_bencListChild( torrents, i );
 
1414
            if( tr_bencDictFindList( torrent, "trackerStats", &trackerStats ) ) {
 
1415
                printTrackersImpl( trackerStats );
 
1416
                if( i+1<n )
 
1417
                    printf( "\n" );
 
1418
            }
 
1419
        }
 
1420
    }
 
1421
}
 
1422
 
 
1423
static void
 
1424
printSession( tr_benc * top )
 
1425
{
 
1426
    tr_benc *args;
 
1427
    if( ( tr_bencDictFindDict( top, "arguments", &args ) ) )
 
1428
    {
 
1429
        int64_t i;
 
1430
        char buf[64];
 
1431
        tr_bool boolVal;
 
1432
        const char * str;
 
1433
 
 
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 );
 
1441
        printf( "\n" );
 
1442
 
 
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 ) ) );
 
1462
        printf( "\n" );
 
1463
 
 
1464
        {
 
1465
            tr_bool altEnabled, altTimeEnabled, upEnabled, downEnabled, seedRatioLimited;
 
1466
            int64_t altDown, altUp, altBegin, altEnd, altDay, upLimit, downLimit, peerLimit;
 
1467
            double seedRatioLimit;
 
1468
 
 
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) )
 
1483
            {
 
1484
                char buf[128];
 
1485
                char buf2[128];
 
1486
                char buf3[128];
 
1487
 
 
1488
                printf( "LIMITS\n" );
 
1489
                printf( "  Peer limit: %" PRId64 "\n", peerLimit );
 
1490
 
 
1491
                if( seedRatioLimited )
 
1492
                    tr_snprintf( buf, sizeof( buf ), "%.2f", seedRatioLimit );
 
1493
                else
 
1494
                    tr_strlcpy( buf, "Unlimited", sizeof( buf ) );
 
1495
                printf( "  Default seed ratio limit: %s\n", buf );
 
1496
 
 
1497
                if( altEnabled )
 
1498
                    tr_formatter_speed_KBps( buf, altUp, sizeof( buf ) );
 
1499
                else if( upEnabled )
 
1500
                    tr_formatter_speed_KBps( buf, upLimit, sizeof( buf ) );
 
1501
                else
 
1502
                    tr_strlcpy( buf, "Unlimited", sizeof( buf ) );
 
1503
                printf( "  Upload speed limit: %s  (%s limit: %s; %s turtle limit: %s)\n",
 
1504
                        buf,
 
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 ) ) );
 
1509
 
 
1510
                if( altEnabled )
 
1511
                    tr_formatter_speed_KBps( buf, altDown, sizeof( buf ) );
 
1512
                else if( downEnabled )
 
1513
                    tr_formatter_speed_KBps( buf, downLimit, sizeof( buf ) );
 
1514
                else
 
1515
                    tr_strlcpy( buf, "Unlimited", sizeof( buf ) );
 
1516
                printf( "  Download speed limit: %s  (%s limit: %s; %s turtle limit: %s)\n",
 
1517
                        buf,
 
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 ) ) );
 
1522
 
 
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 " );
 
1534
                    printf( "\n" );
 
1535
                }
 
1536
            }
 
1537
        }
 
1538
        printf( "\n" );
 
1539
 
 
1540
        printf( "MISC\n" );
 
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" ) );
 
1545
    }
 
1546
}
 
1547
 
 
1548
static void
 
1549
printSessionStats( tr_benc * top )
 
1550
{
 
1551
    tr_benc *args, *d;
 
1552
    if( ( tr_bencDictFindDict( top, "arguments", &args ) ) )
 
1553
    {
 
1554
        char buf[512];
 
1555
        int64_t up, down, secs, sessions;
 
1556
 
 
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 ) )
 
1561
        {
 
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 ) ) );
 
1567
        }
 
1568
 
 
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 ) )
 
1574
        {
 
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 ) ) );
 
1581
        }
 
1582
    }
 
1583
}
 
1584
 
 
1585
static char id[4096];
 
1586
 
 
1587
static int
 
1588
processResponse( const char * host, int port, const void * response, size_t len )
 
1589
{
 
1590
    tr_benc top;
 
1591
    int status = EXIT_SUCCESS;
 
1592
 
 
1593
    if( debug )
 
1594
        fprintf( stderr, "got response (len %d):\n--------\n%*.*s\n--------\n",
 
1595
                 (int)len, (int)len, (int)len, (const char*) response );
 
1596
 
 
1597
    if( tr_jsonParse( NULL, response, len, &top, NULL ) )
 
1598
    {
 
1599
        tr_nerr( MY_NAME, "Unable to parse response \"%*.*s\"", (int)len,
 
1600
                 (int)len, (char*)response );
 
1601
        status |= EXIT_FAILURE;
 
1602
    }
 
1603
    else
 
1604
    {
 
1605
        int64_t      tag = -1;
 
1606
        const char * str;
 
1607
        tr_bencDictFindInt( &top, "tag", &tag );
 
1608
 
 
1609
        switch( tag )
 
1610
        {
 
1611
            case TAG_SESSION:
 
1612
                printSession( &top ); break;
 
1613
 
 
1614
            case TAG_STATS:
 
1615
                printSessionStats( &top ); break;
 
1616
 
 
1617
            case TAG_DETAILS:
 
1618
                printDetails( &top ); break;
 
1619
 
 
1620
            case TAG_FILES:
 
1621
                printFileList( &top ); break;
 
1622
 
 
1623
            case TAG_LIST:
 
1624
                printTorrentList( &top ); break;
 
1625
 
 
1626
            case TAG_PEERS:
 
1627
                printPeers( &top ); break;
 
1628
 
 
1629
            case TAG_PIECES:
 
1630
                printPieces( &top ); break;
 
1631
 
 
1632
            case TAG_PORTTEST:
 
1633
                printPortTest( &top ); break;
 
1634
 
 
1635
            case TAG_TRACKERS:
 
1636
                printTrackers( &top ); break;
 
1637
 
 
1638
            case TAG_TORRENT_ADD: {
 
1639
                int64_t i;
 
1640
                tr_benc * b = &top;
 
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 */
 
1646
            }
 
1647
            default:
 
1648
                if( !tr_bencDictFindStr( &top, "result", &str ) )
 
1649
                    status |= EXIT_FAILURE;
 
1650
                else {
 
1651
                    printf( "%s:%d responded: \"%s\"\n", host, port, str );
 
1652
                    if( strcmp( str, "success") )
 
1653
                        status |= EXIT_FAILURE;
 
1654
                }
 
1655
        }
 
1656
 
 
1657
        tr_bencFree( &top );
 
1658
    }
 
1659
 
 
1660
    return status;
 
1661
}
 
1662
 
 
1663
static CURL*
 
1664
tr_curl_easy_init( struct evbuffer * writebuf )
 
1665
{
 
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 */
 
1676
    if( netrc )
 
1677
        curl_easy_setopt( curl, CURLOPT_NETRC_FILE, netrc );
 
1678
    if( auth )
 
1679
        curl_easy_setopt( curl, CURLOPT_USERPWD, auth );
 
1680
    if( sessionId ) {
 
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 );
 
1684
        /* fixme: leaks */
 
1685
    }
 
1686
    return curl;
 
1687
}
 
1688
 
 
1689
static int
 
1690
flush( const char * host, int port, tr_benc ** benc )
 
1691
{
 
1692
    CURLcode res;
 
1693
    CURL * curl;
 
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 );
 
1698
 
 
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 ) );
 
1703
 
 
1704
    if( debug )
 
1705
        fprintf( stderr, "posting:\n--------\n%s\n--------\n", json );
 
1706
 
 
1707
    if(( res = curl_easy_perform( curl )))
 
1708
    {
 
1709
        tr_nerr( MY_NAME, "(%s) %s", url, curl_easy_strerror( res ) );
 
1710
        status |= EXIT_FAILURE;
 
1711
    }
 
1712
    else
 
1713
    {
 
1714
        long response;
 
1715
        curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &response );
 
1716
        switch( response ) {
 
1717
            case 200:
 
1718
                status |= processResponse( host, port, EVBUFFER_DATA(buf), EVBUFFER_LENGTH(buf) );
 
1719
                break;
 
1720
            case 409:
 
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 );
 
1725
                curl = NULL;
 
1726
                flush( host, port, benc );
 
1727
                benc = NULL;
 
1728
                break;
 
1729
            default:
 
1730
                fprintf( stderr, "Unexpected response: %s\n", (char*)EVBUFFER_DATA(buf) );
 
1731
                status |= EXIT_FAILURE;
 
1732
                break;
 
1733
        }
 
1734
    }
 
1735
 
 
1736
    /* cleanup */
 
1737
    tr_free( url );
 
1738
    tr_free( json );
 
1739
    evbuffer_free( buf );
 
1740
    if( curl != 0 )
 
1741
        curl_easy_cleanup( curl );
 
1742
    if( benc != NULL ) {
 
1743
        tr_bencFree( *benc );
 
1744
        *benc = 0;
 
1745
    }
 
1746
    return status;
 
1747
}
 
1748
 
 
1749
static tr_benc*
 
1750
ensure_sset( tr_benc ** sset )
 
1751
{
 
1752
    tr_benc * args;
 
1753
 
 
1754
    if( *sset )
 
1755
        args = tr_bencDictFind( *sset, ARGUMENTS );
 
1756
    else {
 
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 );
 
1761
    }
 
1762
 
 
1763
    return args;
 
1764
}
 
1765
 
 
1766
static tr_benc*
 
1767
ensure_tset( tr_benc ** tset )
 
1768
{
 
1769
    tr_benc * args;
 
1770
 
 
1771
    if( *tset )
 
1772
        args = tr_bencDictFind( *tset, ARGUMENTS );
 
1773
    else {
 
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 );
 
1778
    }
 
1779
 
 
1780
    return args;
 
1781
}
 
1782
 
 
1783
static int
 
1784
processArgs( const char * host, int port, int argc, const char ** argv )
 
1785
{
 
1786
    int c;
 
1787
    int status = EXIT_SUCCESS;
 
1788
    const char * optarg;
 
1789
    tr_benc *sset = 0;
 
1790
    tr_benc *tset = 0;
 
1791
    tr_benc *tadd = 0;
 
1792
 
 
1793
    *id = '\0';
 
1794
 
 
1795
    while(( c = tr_getopt( getUsage( ), argc, argv, opts, &optarg )))
 
1796
    {
 
1797
        const int stepMode = getOptMode( c );
 
1798
 
 
1799
        if( !stepMode ) /* meta commands */
 
1800
        {
 
1801
            switch( c )
 
1802
            {
 
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 );
 
1812
                    break;
 
1813
 
 
1814
                case 'b': /* debug */
 
1815
                    debug = TRUE;
 
1816
                    break;
 
1817
 
 
1818
                case 'n': /* auth */
 
1819
                    auth = tr_strdup( optarg );
 
1820
                    break;
 
1821
 
 
1822
                case 'N': /* netrc */
 
1823
                    netrc = tr_strdup( optarg );
 
1824
                    break;
 
1825
 
 
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 ) );
 
1830
                    break;
 
1831
 
 
1832
                case 'V': /* show version number */
 
1833
                    fprintf( stderr, "%s %s\n", MY_NAME, LONG_VERSION_STRING );
 
1834
                    exit( 0 );
 
1835
                    break;
 
1836
 
 
1837
                case TR_OPT_ERR:
 
1838
                    fprintf( stderr, "invalid option\n" );
 
1839
                    showUsage( );
 
1840
                    status |= EXIT_FAILURE;
 
1841
                    break;
 
1842
 
 
1843
                case TR_OPT_UNK:
 
1844
                    if( tadd ) {
 
1845
                        tr_benc * args = tr_bencDictFind( tadd, ARGUMENTS );
 
1846
                        char * tmp = getEncodedMetainfo( optarg );
 
1847
                        if( tmp )
 
1848
                            tr_bencDictAddStr( args, "metainfo", tmp );
 
1849
                        else
 
1850
                            tr_bencDictAddStr( args, "filename", optarg );
 
1851
                        tr_free( tmp );
 
1852
                    } else {
 
1853
                        fprintf( stderr, "Unknown option: %s\n", optarg );
 
1854
                        status |= EXIT_FAILURE;
 
1855
                    }
 
1856
                    break;
 
1857
            }
 
1858
        }
 
1859
        else if( stepMode == MODE_TORRENT_GET )
 
1860
        {
 
1861
            size_t i, n;
 
1862
            tr_benc * top = tr_new0( tr_benc, 1 );
 
1863
            tr_benc * args;
 
1864
            tr_benc * fields;
 
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 );
 
1869
 
 
1870
            if( tset != 0 ) { addIdArg( tr_bencDictFind( tset, ARGUMENTS ), id ); status |= flush( host, port, &tset ); }
 
1871
            
 
1872
            switch( c )
 
1873
            {
 
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 );
 
1878
                          break;
 
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] );
 
1882
                          break;
 
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 );
 
1887
                          break;
 
1888
                case 941: tr_bencDictAddInt( top, "tag", TAG_PEERS );
 
1889
                          tr_bencListAddStr( fields, "peers" );
 
1890
                          addIdArg( args, id );
 
1891
                          break;
 
1892
                case 942: tr_bencDictAddInt( top, "tag", TAG_PIECES );
 
1893
                          tr_bencListAddStr( fields, "pieces" );
 
1894
                          tr_bencListAddStr( fields, "pieceCount" );
 
1895
                          addIdArg( args, id );
 
1896
                          break;
 
1897
                case 943: tr_bencDictAddInt( top, "tag", TAG_TRACKERS );
 
1898
                          tr_bencListAddStr( fields, "trackerStats" );
 
1899
                          addIdArg( args, id );
 
1900
                          break;
 
1901
                default:  assert( "unhandled value" && 0 );
 
1902
            }
 
1903
 
 
1904
            status |= flush( host, port, &top );
 
1905
        }
 
1906
        else if( stepMode == MODE_SESSION_SET )
 
1907
        {
 
1908
            tr_benc * args = ensure_sset( &sset );
 
1909
 
 
1910
            switch( c )
 
1911
            {
 
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 );
 
1914
                          break;
 
1915
                case 801: tr_bencDictAddBool( args, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, FALSE );
 
1916
                          break;
 
1917
                case 970: tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_ENABLED, TRUE );
 
1918
                          break;
 
1919
                case 971: tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_ENABLED, FALSE );
 
1920
                          break;
 
1921
                case 972: tr_bencDictAddInt( args, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, numarg( optarg ) );
 
1922
                          break;
 
1923
                case 973: tr_bencDictAddInt( args, TR_PREFS_KEY_ALT_SPEED_UP_KBps, numarg( optarg ) );
 
1924
                          break;
 
1925
                case 974: tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, TRUE );
 
1926
                          break;
 
1927
                case 975: tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, FALSE );
 
1928
                          break;
 
1929
                case 976: addTime( args, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, optarg );
 
1930
                          break;
 
1931
                case 977: addTime( args, TR_PREFS_KEY_ALT_SPEED_TIME_END, optarg );
 
1932
                          break;
 
1933
                case 978: addDays( args, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, optarg );
 
1934
                          break;
 
1935
                case 'c': tr_bencDictAddStr( args, TR_PREFS_KEY_INCOMPLETE_DIR, optarg );
 
1936
                          tr_bencDictAddBool( args, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, TRUE );
 
1937
                          break;
 
1938
                case 'C': tr_bencDictAddBool( args, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, FALSE );
 
1939
                          break;
 
1940
                case 'e': tr_bencDictAddInt( args, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, atoi(optarg) );
 
1941
                          break;
 
1942
                case 910: tr_bencDictAddStr( args, TR_PREFS_KEY_ENCRYPTION, "required" );
 
1943
                          break;
 
1944
                case 911: tr_bencDictAddStr( args, TR_PREFS_KEY_ENCRYPTION, "preferred" );
 
1945
                          break;
 
1946
                case 912: tr_bencDictAddStr( args, TR_PREFS_KEY_ENCRYPTION, "tolerated" );
 
1947
                          break;
 
1948
                case 'm': tr_bencDictAddBool( args, TR_PREFS_KEY_PORT_FORWARDING, TRUE );
 
1949
                          break;
 
1950
                case 'M': tr_bencDictAddBool( args, TR_PREFS_KEY_PORT_FORWARDING, FALSE );
 
1951
                          break;
 
1952
                case 'o': tr_bencDictAddBool( args, TR_PREFS_KEY_DHT_ENABLED, TRUE );
 
1953
                          break;
 
1954
                case 'O': tr_bencDictAddBool( args, TR_PREFS_KEY_DHT_ENABLED, FALSE );
 
1955
                          break;
 
1956
                case 'p': tr_bencDictAddInt( args, TR_PREFS_KEY_PEER_PORT, numarg( optarg ) );
 
1957
                          break;
 
1958
                case 'P': tr_bencDictAddBool( args, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, TRUE);
 
1959
                          break;
 
1960
                case 'x': tr_bencDictAddBool( args, TR_PREFS_KEY_PEX_ENABLED, TRUE );
 
1961
                          break;
 
1962
                case 'X': tr_bencDictAddBool( args, TR_PREFS_KEY_PEX_ENABLED, FALSE );
 
1963
                          break;
 
1964
                case 'y': tr_bencDictAddBool( args, TR_PREFS_KEY_LPD_ENABLED, TRUE );
 
1965
                          break;
 
1966
                case 'Y': tr_bencDictAddBool( args, TR_PREFS_KEY_LPD_ENABLED, FALSE );
 
1967
                          break;
 
1968
                case 953: tr_bencDictAddReal( args, "seedRatioLimit", atof(optarg) );
 
1969
                          tr_bencDictAddBool( args, "seedRatioLimited", TRUE );
 
1970
                          break;
 
1971
                case 954: tr_bencDictAddBool( args, "seedRatioLimited", FALSE );
 
1972
                          break;
 
1973
                case 990: tr_bencDictAddBool( args, TR_PREFS_KEY_START, FALSE );
 
1974
                          break;
 
1975
                case 991: tr_bencDictAddBool( args, TR_PREFS_KEY_START, TRUE );
 
1976
                          break;
 
1977
                case 992: tr_bencDictAddBool( args, TR_PREFS_KEY_TRASH_ORIGINAL, TRUE );
 
1978
                          break;
 
1979
                case 993: tr_bencDictAddBool( args, TR_PREFS_KEY_TRASH_ORIGINAL, FALSE );
 
1980
                          break;
 
1981
                default:  assert( "unhandled value" && 0 );
 
1982
                          break;
 
1983
            }
 
1984
        }
 
1985
        else if( stepMode == ( MODE_SESSION_SET | MODE_TORRENT_SET ) )
 
1986
        {
 
1987
            tr_benc * targs = 0;
 
1988
            tr_benc * sargs = 0;
 
1989
 
 
1990
            if( *id )
 
1991
                targs = ensure_tset( &tset );
 
1992
            else
 
1993
                sargs = ensure_sset( &sset );
 
1994
            
 
1995
            switch( c )
 
1996
            {
 
1997
                case 'd': if( targs ) {
 
1998
                              tr_bencDictAddInt( targs, "downloadLimit", numarg( optarg ) );
 
1999
                              tr_bencDictAddBool( targs, "downloadLimited", TRUE );
 
2000
                          } else {
 
2001
                              tr_bencDictAddInt( sargs, TR_PREFS_KEY_DSPEED_KBps, numarg( optarg ) );
 
2002
                              tr_bencDictAddBool( sargs, TR_PREFS_KEY_DSPEED_ENABLED, TRUE );
 
2003
                          }
 
2004
                          break;
 
2005
                case 'D': if( targs )
 
2006
                              tr_bencDictAddBool( targs, "downloadLimited", FALSE );
 
2007
                          else
 
2008
                              tr_bencDictAddBool( sargs, TR_PREFS_KEY_DSPEED_ENABLED, FALSE );
 
2009
                          break;
 
2010
                case 'u': if( targs ) {
 
2011
                              tr_bencDictAddInt( targs, "uploadLimit", numarg( optarg ) );
 
2012
                              tr_bencDictAddBool( targs, "uploadLimited", TRUE );
 
2013
                          } else {
 
2014
                              tr_bencDictAddInt( sargs, TR_PREFS_KEY_USPEED_KBps, numarg( optarg ) );
 
2015
                              tr_bencDictAddBool( sargs, TR_PREFS_KEY_USPEED_ENABLED, TRUE );
 
2016
                          }
 
2017
                          break;
 
2018
                case 'U': if( targs )
 
2019
                              tr_bencDictAddBool( targs, "uploadLimited", FALSE );
 
2020
                          else
 
2021
                              tr_bencDictAddBool( sargs, TR_PREFS_KEY_USPEED_ENABLED, FALSE );
 
2022
                          break;
 
2023
                case 930: if( targs )
 
2024
                              tr_bencDictAddInt( targs, "peer-limit", atoi(optarg) );
 
2025
                          else
 
2026
                              tr_bencDictAddInt( sargs, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, atoi(optarg) );
 
2027
                          break;
 
2028
                default:  assert( "unhandled value" && 0 );
 
2029
                          break;
 
2030
            }
 
2031
        }
 
2032
        else if( stepMode == MODE_TORRENT_SET )
 
2033
        {
 
2034
            tr_benc * args = ensure_tset( &tset );
 
2035
 
 
2036
            switch( c )
 
2037
            {
 
2038
                case 712: tr_bencListAddInt( tr_bencDictAddList( args, "trackerRemove", 1 ), atoi( optarg ) );
 
2039
                          break;
 
2040
                case 950: tr_bencDictAddReal( args, "seedRatioLimit", atof(optarg) );
 
2041
                          tr_bencDictAddInt( args, "seedRatioMode", TR_RATIOLIMIT_SINGLE );
 
2042
                          break;
 
2043
                case 951: tr_bencDictAddInt( args, "seedRatioMode", TR_RATIOLIMIT_GLOBAL );
 
2044
                          break;
 
2045
                case 952: tr_bencDictAddInt( args, "seedRatioMode", TR_RATIOLIMIT_UNLIMITED );
 
2046
                          break;
 
2047
                case 984: tr_bencDictAddBool( args, "honorsSessionLimits", TRUE );
 
2048
                          break;
 
2049
                case 985: tr_bencDictAddBool( args, "honorsSessionLimits", FALSE );
 
2050
                          break;
 
2051
                default:  assert( "unhandled value" && 0 );
 
2052
                          break;
 
2053
            }
 
2054
        }
 
2055
        else if( stepMode == ( MODE_TORRENT_SET | MODE_TORRENT_ADD ) )
 
2056
        {
 
2057
            tr_benc * args;
 
2058
 
 
2059
            if( tadd )
 
2060
                args = tr_bencDictFind( tadd, ARGUMENTS );
 
2061
            else
 
2062
                args = ensure_tset( &tset );
 
2063
       
 
2064
            switch( c )
 
2065
            {         
 
2066
                case 'g': addFiles( args, "files-wanted", optarg );
 
2067
                          break;
 
2068
                case 'G': addFiles( args, "files-unwanted", optarg );
 
2069
                          break;
 
2070
                case 900: addFiles( args, "priority-high", optarg );
 
2071
                          break;
 
2072
                case 901: addFiles( args, "priority-normal", optarg );
 
2073
                          break;
 
2074
                case 902: addFiles( args, "priority-low", optarg );
 
2075
                          break;
 
2076
                case 700: tr_bencDictAddInt( args, "bandwidthPriority",  1 );
 
2077
                          break;
 
2078
                case 701: tr_bencDictAddInt( args, "bandwidthPriority",  0 );
 
2079
                          break;
 
2080
                case 702: tr_bencDictAddInt( args, "bandwidthPriority", -1 );
 
2081
                          break;
 
2082
                case 710: tr_bencListAddStr( tr_bencDictAddList( args, "trackerAdd", 1 ), optarg );
 
2083
                          break;
 
2084
                default:  assert( "unhandled value" && 0 );
 
2085
                          break;
 
2086
            }
 
2087
        }
 
2088
        else if( c == 961 ) /* set location */
 
2089
        {
 
2090
            if( tadd )
 
2091
            {
 
2092
                tr_benc * args = tr_bencDictFind( tadd, ARGUMENTS );
 
2093
                tr_bencDictAddStr( args, "download-dir", optarg );
 
2094
            }
 
2095
            else
 
2096
            {
 
2097
                tr_benc * args;
 
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 );
 
2106
                break;
 
2107
            }
 
2108
        }
 
2109
        else switch( c )
 
2110
        {
 
2111
            case 920: /* session-info */
 
2112
            {
 
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 );
 
2118
                break;
 
2119
            }
 
2120
            case 's': /* start */
 
2121
            {
 
2122
                if( tadd )
 
2123
                    tr_bencDictAddBool( tr_bencDictFind( tadd, "arguments" ), "paused", FALSE );
 
2124
                else {
 
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 );
 
2130
                }
 
2131
                break;
 
2132
            }
 
2133
            case 'S': /* stop */
 
2134
            {
 
2135
                if( tadd )
 
2136
                    tr_bencDictAddBool( tr_bencDictFind( tadd, "arguments" ), "paused", TRUE );
 
2137
                else {
 
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 );
 
2143
                }
 
2144
                break;
 
2145
            }
 
2146
            case 'w':
 
2147
            {
 
2148
                char * path = absolutify( optarg );
 
2149
                if( tadd )
 
2150
                    tr_bencDictAddStr( tr_bencDictFind( tadd, "arguments" ), "download-dir", path );
 
2151
                else {
 
2152
                    tr_benc * args = ensure_sset( &sset );
 
2153
                    tr_bencDictAddStr( args, "download-dir", path );
 
2154
                }
 
2155
                tr_free( path );
 
2156
                break;
 
2157
            }
 
2158
            case 963:
 
2159
            {
 
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 );
 
2164
                break;
 
2165
            }
 
2166
            case 921:
 
2167
            {
 
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 );
 
2173
                break;
 
2174
            }
 
2175
            case 962:
 
2176
            {
 
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 );
 
2182
                break;
 
2183
            }
 
2184
            case 'v':
 
2185
            {
 
2186
                tr_benc * 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 );
 
2193
                break;
 
2194
            }
 
2195
            case 'r':
 
2196
            case 'R':
 
2197
            {
 
2198
                tr_benc * args;
 
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 );
 
2206
                break;
 
2207
            }
 
2208
            case 960:
 
2209
            {
 
2210
                tr_benc * args;
 
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 );
 
2219
                break;
 
2220
            }
 
2221
            default:
 
2222
            {
 
2223
                fprintf( stderr, "got opt [%d]\n", (int)c );
 
2224
                showUsage( );
 
2225
                break;
 
2226
            }
 
2227
        }
 
2228
    }
 
2229
 
 
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 );
 
2233
    return status;
 
2234
}
 
2235
 
 
2236
/* [host:port] or [host] or [port] */
 
2237
static void
 
2238
getHostAndPort( int * argc, char ** argv, char ** host, int * port )
 
2239
{
 
2240
    if( *argv[1] != '-' )
 
2241
    {
 
2242
        int          i;
 
2243
        const char * s = argv[1];
 
2244
        const char * delim = strchr( s, ':' );
 
2245
        if( delim )   /* user passed in both host and port */
 
2246
        {
 
2247
            *host = tr_strndup( s, delim - s );
 
2248
            *port = atoi( delim + 1 );
 
2249
        }
 
2250
        else
 
2251
        {
 
2252
            char *    end;
 
2253
            const int i = strtol( s, &end, 10 );
 
2254
            if( !*end ) /* user passed in a port */
 
2255
                *port = i;
 
2256
            else /* user passed in a host */
 
2257
                *host = tr_strdup( s );
 
2258
        }
 
2259
 
 
2260
        *argc -= 1;
 
2261
        for( i = 1; i < *argc; ++i )
 
2262
            argv[i] = argv[i + 1];
 
2263
    }
 
2264
}
 
2265
 
 
2266
int
 
2267
main( int argc, char ** argv )
 
2268
{
 
2269
    int port = DEFAULT_PORT;
 
2270
    char * host = NULL;
 
2271
    int exit_status = EXIT_SUCCESS;
 
2272
 
 
2273
    if( argc < 2 ) {
 
2274
        showUsage( );
 
2275
        return EXIT_FAILURE;
 
2276
    }
 
2277
 
 
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 );
 
2281
 
 
2282
    getHostAndPort( &argc, argv, &host, &port );
 
2283
    if( host == NULL )
 
2284
        host = tr_strdup( DEFAULT_HOST );
 
2285
 
 
2286
    exit_status = processArgs( host, port, argc, (const char**)argv );
 
2287
 
 
2288
    tr_free( host );
 
2289
    return exit_status;
 
2290
}