source: trunk/daemon/remote.c @ 13195

Last change on this file since 13195 was 13195, checked in by jordan, 10 years ago

(trunk) use base-10 units for network bandwidth (ie, speed) and disk sizes.

It looks like the Mac client is already doing this and it's clearly the trend in other apps as well. Even apt-get is using kB/s, ferchrissake... :)

Flame away.

  • Property svn:keywords set to Date Rev Author Id
File size: 89.8 KB
Line 
1/*
2 * This file Copyright (C) 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 13195 2012-02-03 21:21:52Z jordan $
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 <event2/buffer.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#define DEFAULT_URL TR_DEFAULT_RPC_URL_STR "rpc/"
44
45#define ARGUMENTS "arguments"
46
47#define MEM_K 1024
48#define MEM_B_STR   "B"
49#define MEM_K_STR "KiB"
50#define MEM_M_STR "MiB"
51#define MEM_G_STR "GiB"
52#define MEM_T_STR "TiB"
53
54#define DISK_K 1000
55#define DISK_B_STR  "B"
56#define DISK_K_STR "kB"
57#define DISK_M_STR "MB"
58#define DISK_G_STR "GB"
59#define DISK_T_STR "TB"
60
61#define SPEED_K 1000
62#define SPEED_B_STR  "B/s"
63#define SPEED_K_STR "kB/s"
64#define SPEED_M_STR "MB/s"
65#define SPEED_G_STR "GB/s"
66#define SPEED_T_STR "TB/s"
67
68/***
69****
70****  Display Utilities
71****
72***/
73
74static void
75etaToString( char *  buf, size_t  buflen, int64_t eta )
76{
77    if( eta < 0 )
78        tr_snprintf( buf, buflen, "Unknown" );
79    else if( eta < 60 )
80        tr_snprintf( buf, buflen, "%" PRId64 " sec", eta );
81    else if( eta < ( 60 * 60 ) )
82        tr_snprintf( buf, buflen, "%" PRId64 " min", eta / 60 );
83    else if( eta < ( 60 * 60 * 24 ) )
84        tr_snprintf( buf, buflen, "%" PRId64 " hrs", eta / ( 60 * 60 ) );
85    else
86        tr_snprintf( buf, buflen, "%" PRId64 " days", eta / ( 60 * 60 * 24 ) );
87}
88
89static char*
90tr_strltime( char * buf, int seconds, size_t buflen )
91{
92    int  days, hours, minutes;
93    char d[128], h[128], m[128], s[128];
94
95    if( seconds < 0 )
96        seconds = 0;
97
98    days = seconds / 86400;
99    hours = ( seconds % 86400 ) / 3600;
100    minutes = ( seconds % 3600 ) / 60;
101    seconds = ( seconds % 3600 ) % 60;
102
103    tr_snprintf( d, sizeof( d ), "%d %s", days, days==1?"day":"days" );
104    tr_snprintf( h, sizeof( h ), "%d %s", hours, hours==1?"hour":"hours" );
105    tr_snprintf( m, sizeof( m ), "%d %s", minutes, minutes==1?"minute":"minutes" );
106    tr_snprintf( s, sizeof( s ), "%d %s", seconds, seconds==1?"seconds":"seconds" );
107
108    if( days )
109    {
110        if( days >= 4 || !hours )
111            tr_strlcpy( buf, d, buflen );
112        else
113            tr_snprintf( buf, buflen, "%s, %s", d, h );
114    }
115    else if( hours )
116    {
117        if( hours >= 4 || !minutes )
118            tr_strlcpy( buf, h, buflen );
119        else
120            tr_snprintf( buf, buflen, "%s, %s", h, m );
121    }
122    else if( minutes )
123    {
124        if( minutes >= 4 || !seconds )
125            tr_strlcpy( buf, m, buflen );
126        else
127            tr_snprintf( buf, buflen, "%s, %s", m, s );
128    }
129    else tr_strlcpy( buf, s, buflen );
130
131    return buf;
132}
133
134static char*
135strlpercent( char * buf, double x, size_t buflen )
136{
137    return tr_strpercent( buf, x, buflen );
138}
139
140static char*
141strlratio2( char * buf, double ratio, size_t buflen )
142{
143    return tr_strratio( buf, buflen, ratio, "Inf" );
144}
145
146static char*
147strlratio( char * buf, int64_t numerator, int64_t denominator, size_t buflen )
148{
149    double ratio;
150
151    if( denominator != 0 )
152        ratio = numerator / (double)denominator;
153    else if( numerator != 0 )
154        ratio = TR_RATIO_INF;
155    else
156        ratio = TR_RATIO_NA;
157
158    return strlratio2( buf, ratio, buflen );
159}
160
161static char*
162strlmem( char * buf, int64_t bytes, size_t buflen )
163{
164    if( !bytes )
165        tr_strlcpy( buf, "None", buflen );
166    else
167        tr_formatter_mem_B( buf, bytes, buflen );
168
169    return buf;
170}
171
172static char*
173strlsize( char * buf, int64_t bytes, size_t buflen )
174{
175    if( bytes < 0 )
176        tr_strlcpy( buf, "Unknown", buflen );
177    else if( bytes == 0 )
178        tr_strlcpy( buf, "None", buflen );
179    else
180        tr_formatter_size_B( buf, bytes, buflen );
181
182    return buf;
183}
184
185enum
186{
187    TAG_SESSION,
188    TAG_STATS,
189    TAG_DETAILS,
190    TAG_FILES,
191    TAG_LIST,
192    TAG_PEERS,
193    TAG_PIECES,
194    TAG_PORTTEST,
195    TAG_TORRENT_ADD,
196    TAG_TRACKERS
197};
198
199static const char*
200getUsage( void )
201{
202    return
203        MY_NAME" "LONG_VERSION_STRING"\n"
204        "A fast and easy BitTorrent client\n"
205        "http://www.transmissionbt.com/\n"
206        "\n"
207        "Usage: " MY_NAME
208        " [host] [options]\n"
209        "       "
210        MY_NAME " [port] [options]\n"
211                "       "
212        MY_NAME " [host:port] [options]\n"
213                "       "
214        MY_NAME " [http(s?)://host:port/transmission/] [options]\n"
215                "\n"
216                "See the man page for detailed explanations and many examples.";
217}
218
219/***
220****
221****  Command-Line Arguments
222****
223***/
224
225static tr_option opts[] =
226{
227    { 'a', "add",                    "Add torrent files by filename or URL", "a",  0, NULL },
228    { 970, "alt-speed",              "Use the alternate Limits", "as",  0, NULL },
229    { 971, "no-alt-speed",           "Don't use the alternate Limits", "AS",  0, NULL },
230    { 972, "alt-speed-downlimit",    "max alternate download speed (in "SPEED_K_STR")", "asd",  1, "<speed>" },
231    { 973, "alt-speed-uplimit",      "max alternate upload speed (in "SPEED_K_STR")", "asu",  1, "<speed>" },
232    { 974, "alt-speed-scheduler",    "Use the scheduled on/off times", "asc",  0, NULL },
233    { 975, "no-alt-speed-scheduler", "Don't use the scheduled on/off times", "ASC",  0, NULL },
234    { 976, "alt-speed-time-begin",   "Time to start using the alt speed limits (in hhmm)", NULL,  1, "<time>" },
235    { 977, "alt-speed-time-end",     "Time to stop using the alt speed limits (in hhmm)", NULL,  1, "<time>" },
236    { 978, "alt-speed-days",         "Numbers for any/all days of the week - eg. \"1-7\"", NULL,  1, "<days>" },
237    { 963, "blocklist-update",       "Blocklist update", NULL, 0, NULL },
238    { 'c', "incomplete-dir",         "Where to store new torrents until they're complete", "c", 1, "<dir>" },
239    { 'C', "no-incomplete-dir",      "Don't store incomplete torrents in a different location", "C", 0, NULL },
240    { 'b', "debug",                  "Print debugging information", "b",  0, NULL },
241    { 'd', "downlimit",              "Set the max download speed in "SPEED_K_STR" for the current torrent(s) or globally", "d", 1, "<speed>" },
242    { 'D', "no-downlimit",           "Disable max download speed for the current torrent(s) or globally", "D", 0, NULL },
243    { 'e', "cache",                  "Set the maximum size of the session's memory cache (in " MEM_M_STR ")", "e", 1, "<size>" },
244    { 910, "encryption-required",    "Encrypt all peer connections", "er", 0, NULL },
245    { 911, "encryption-preferred",   "Prefer encrypted peer connections", "ep", 0, NULL },
246    { 912, "encryption-tolerated",   "Prefer unencrypted peer connections", "et", 0, NULL },
247    { 850, "exit",                   "Tell the transmission session to shut down", NULL, 0, NULL },
248    { 940, "files",                  "List the current torrent(s)' files", "f",  0, NULL },
249    { 'g', "get",                    "Mark files for download", "g",  1, "<files>" },
250    { 'G', "no-get",                 "Mark files for not downloading", "G",  1, "<files>" },
251    { 'i', "info",                   "Show the current torrent(s)' details", "i",  0, NULL },
252    { 940, "info-files",             "List the current torrent(s)' files", "if",  0, NULL },
253    { 941, "info-peers",             "List the current torrent(s)' peers", "ip",  0, NULL },
254    { 942, "info-pieces",            "List the current torrent(s)' pieces", "ic",  0, NULL },
255    { 943, "info-trackers",          "List the current torrent(s)' trackers", "it",  0, NULL },
256    { 920, "session-info",           "Show the session's details", "si", 0, NULL },
257    { 921, "session-stats",          "Show the session's statistics", "st", 0, NULL },
258    { 'l', "list",                   "List all torrents", "l",  0, NULL },
259    { 960, "move",                   "Move current torrent's data to a new folder", NULL, 1, "<path>" },
260    { 961, "find",                   "Tell Transmission where to find a torrent's data", NULL, 1, "<path>" },
261    { 'm', "portmap",                "Enable portmapping via NAT-PMP or UPnP", "m",  0, NULL },
262    { 'M', "no-portmap",             "Disable portmapping", "M",  0, NULL },
263    { 'n', "auth",                   "Set username and password", "n",  1, "<user:pw>" },
264    { 810, "authenv",                "Set authentication info from the TR_AUTH environment variable (user:pw)", "ne", 0, NULL },
265    { 'N', "netrc",                  "Set authentication info from a .netrc file", "N",  1, "<file>" },
266    { 820, "ssl",                    "Use SSL when talking to daemon", NULL,  0, NULL },
267    { 'o', "dht",                    "Enable distributed hash tables (DHT)", "o", 0, NULL },
268    { 'O', "no-dht",                 "Disable distributed hash tables (DHT)", "O", 0, NULL },
269    { 'p', "port",                   "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "p", 1, "<port>" },
270    { 962, "port-test",              "Port testing", "pt", 0, NULL },
271    { 'P', "random-port",            "Random port for incomping peers", "P", 0, NULL },
272    { 900, "priority-high",          "Try to download these file(s) first", "ph", 1, "<files>" },
273    { 901, "priority-normal",        "Try to download these file(s) normally", "pn", 1, "<files>" },
274    { 902, "priority-low",           "Try to download these file(s) last", "pl", 1, "<files>" },
275    { 700, "bandwidth-high",         "Give this torrent first chance at available bandwidth", "Bh", 0, NULL },
276    { 701, "bandwidth-normal",       "Give this torrent bandwidth left over by high priority torrents", "Bn", 0, NULL },
277    { 702, "bandwidth-low",          "Give this torrent bandwidth left over by high and normal priority torrents", "Bl", 0, NULL },
278    { 600, "reannounce",             "Reannounce the current torrent(s)", NULL,  0, NULL },
279    { 'r', "remove",                 "Remove the current torrent(s)", "r",  0, NULL },
280    { 930, "peers",                  "Set the maximum number of peers for the current torrent(s) or globally", "pr", 1, "<max>" },
281    { 'R', "remove-and-delete",      "Remove the current torrent(s) and delete local data", NULL, 0, NULL },
282    { 800, "torrent-done-script",    "Specify a script to run when a torrent finishes", NULL, 1, "<file>" },
283    { 801, "no-torrent-done-script", "Don't run a script when torrents finish", NULL, 0, NULL },
284    { 950, "seedratio",              "Let the current torrent(s) seed until a specific ratio", "sr", 1, "ratio" },
285    { 951, "seedratio-default",      "Let the current torrent(s) use the global seedratio settings", "srd", 0, NULL },
286    { 952, "no-seedratio",           "Let the current torrent(s) seed regardless of ratio", "SR", 0, NULL },
287    { 953, "global-seedratio",       "All torrents, unless overridden by a per-torrent setting, should seed until a specific ratio", "gsr", 1, "ratio" },
288    { 954, "no-global-seedratio",    "All torrents, unless overridden by a per-torrent setting, should seed regardless of ratio", "GSR", 0, NULL },
289    { 710, "tracker-add",            "Add a tracker to a torrent", "td", 1, "<tracker>" },
290    { 712, "tracker-remove",         "Remove a tracker from a torrent", "tr", 1, "<trackerId>" },
291    { 's', "start",                  "Start the current torrent(s)", "s",  0, NULL },
292    { 'S', "stop",                   "Stop the current torrent(s)", "S",  0, NULL },
293    { 't', "torrent",                "Set the current torrent(s)", "t",  1, "<torrent>" },
294    { 990, "start-paused",           "Start added torrents paused", NULL, 0, NULL },
295    { 991, "no-start-paused",        "Start added torrents unpaused", NULL, 0, NULL },
296    { 992, "trash-torrent",          "Delete torrents after adding", NULL, 0, NULL },
297    { 993, "no-trash-torrent",       "Do not delete torrents after adding", NULL, 0, NULL },
298    { 984, "honor-session",          "Make the current torrent(s) honor the session limits", "hl",  0, NULL },
299    { 985, "no-honor-session",       "Make the current torrent(s) not honor the session limits", "HL",  0, NULL },
300    { 'u', "uplimit",                "Set the max upload speed in "SPEED_K_STR" for the current torrent(s) or globally", "u", 1, "<speed>" },
301    { 'U', "no-uplimit",             "Disable max upload speed for the current torrent(s) or globally", "U", 0, NULL },
302    { 830, "utp",                    "Enable uTP for peer connections", NULL, 0, NULL },
303    { 831, "no-utp",                 "Disable uTP for peer connections", NULL, 0, NULL },
304    { 'v', "verify",                 "Verify the current torrent(s)", "v",  0, NULL },
305    { 'V', "version",                "Show version number and exit", "V", 0, NULL },
306    { 'w', "download-dir",           "When adding a new torrent, set its download folder. Otherwise, set the default download folder", "w",  1, "<path>" },
307    { 'x', "pex",                    "Enable peer exchange (PEX)", "x",  0, NULL },
308    { 'X', "no-pex",                 "Disable peer exchange (PEX)", "X",  0, NULL },
309    { 'y', "lpd",                    "Enable local peer discovery (LPD)", "y",  0, NULL },
310    { 'Y', "no-lpd",                 "Disable local peer discovery (LPD)", "Y",  0, NULL },
311    { 941, "peer-info",              "List the current torrent(s)' peers", "pi",  0, NULL },
312    {   0, NULL,                     NULL, NULL, 0, NULL }
313};
314
315static void
316showUsage( void )
317{
318    tr_getopt_usage( MY_NAME, getUsage( ), opts );
319}
320
321static int
322numarg( const char * arg )
323{
324    char *     end = NULL;
325    const long num = strtol( arg, &end, 10 );
326
327    if( *end )
328    {
329        fprintf( stderr, "Not a number: \"%s\"\n", arg );
330        showUsage( );
331        exit( EXIT_FAILURE );
332    }
333    return num;
334}
335
336enum
337{
338    MODE_TORRENT_START         = (1<<0),
339    MODE_TORRENT_STOP          = (1<<1),
340    MODE_TORRENT_VERIFY        = (1<<2),
341    MODE_TORRENT_REANNOUNCE    = (1<<3),
342    MODE_TORRENT_SET           = (1<<4),
343    MODE_TORRENT_GET           = (1<<5),
344    MODE_TORRENT_ADD           = (1<<6),
345    MODE_TORRENT_REMOVE        = (1<<7),
346    MODE_TORRENT_SET_LOCATION  = (1<<8),
347    MODE_SESSION_SET           = (1<<9),
348    MODE_SESSION_GET           = (1<<10),
349    MODE_SESSION_STATS         = (1<<11),
350    MODE_SESSION_CLOSE         = (1<<12),
351    MODE_BLOCKLIST_UPDATE      = (1<<13),
352    MODE_PORT_TEST             = (1<<14)
353};
354
355static int
356getOptMode( int val )
357{
358    switch( val )
359    {
360        case TR_OPT_ERR:
361        case TR_OPT_UNK:
362        case 'a': /* add torrent */
363        case 'b': /* debug */
364        case 'n': /* auth */
365        case 810: /* authenv */
366        case 'N': /* netrc */
367        case 820: /* UseSSL */
368        case 't': /* set current torrent */
369        case 'V': /* show version number */
370            return 0;
371
372        case 'c': /* incomplete-dir */
373        case 'C': /* no-incomplete-dir */
374        case 'e': /* cache */
375        case 'm': /* portmap */
376        case 'M': /* "no-portmap */
377        case 'o': /* dht */
378        case 'O': /* no-dht */
379        case 'p': /* incoming peer port */
380        case 'P': /* random incoming peer port */
381        case 'x': /* pex */
382        case 'X': /* no-pex */
383        case 'y': /* lpd */
384        case 'Y': /* no-lpd */
385        case 800: /* torrent-done-script */
386        case 801: /* no-torrent-done-script */
387        case 830: /* utp */
388        case 831: /* no-utp */
389        case 970: /* alt-speed */
390        case 971: /* no-alt-speed */
391        case 972: /* alt-speed-downlimit */
392        case 973: /* alt-speed-uplimit */
393        case 974: /* alt-speed-scheduler */
394        case 975: /* no-alt-speed-scheduler */
395        case 976: /* alt-speed-time-begin */
396        case 977: /* alt-speed-time-end */
397        case 978: /* alt-speed-days */
398        case 910: /* encryption-required */
399        case 911: /* encryption-preferred */
400        case 912: /* encryption-tolerated */
401        case 953: /* global-seedratio */
402        case 954: /* no-global-seedratio */
403        case 990: /* start-paused */
404        case 991: /* no-start-paused */
405        case 992: /* trash-torrent */
406        case 993: /* no-trash-torrent */
407            return MODE_SESSION_SET;
408
409        case 712: /* tracker-remove */
410        case 950: /* seedratio */
411        case 951: /* seedratio-default */
412        case 952: /* no-seedratio */
413        case 984: /* honor-session */
414        case 985: /* no-honor-session */
415            return MODE_TORRENT_SET;
416
417        case 920: /* session-info */
418            return MODE_SESSION_GET;
419
420        case 'g': /* get */
421        case 'G': /* no-get */
422        case 700: /* torrent priority-high */
423        case 701: /* torrent priority-normal */
424        case 702: /* torrent priority-low */
425        case 710: /* tracker-add */
426        case 900: /* file priority-high */
427        case 901: /* file priority-normal */
428        case 902: /* file priority-low */
429            return MODE_TORRENT_SET | MODE_TORRENT_ADD;
430
431        case 961: /* find */
432            return MODE_TORRENT_SET_LOCATION | MODE_TORRENT_ADD;
433
434        case 'i': /* info */
435        case 'l': /* list all torrents */
436        case 940: /* info-files */
437        case 941: /* info-peer */
438        case 942: /* info-pieces */
439        case 943: /* info-tracker */
440            return MODE_TORRENT_GET;
441
442        case 'd': /* download speed limit */
443        case 'D': /* no download speed limit */
444        case 'u': /* upload speed limit */
445        case 'U': /* no upload speed limit */
446        case 930: /* peers */
447            return MODE_SESSION_SET | MODE_TORRENT_SET;
448
449        case 's': /* start */
450            return MODE_TORRENT_START | MODE_TORRENT_ADD;
451
452        case 'S': /* stop */
453            return MODE_TORRENT_STOP | MODE_TORRENT_ADD;
454
455        case 'w': /* download-dir */
456            return MODE_SESSION_SET | MODE_TORRENT_ADD;
457
458        case 850: /* session-close */
459            return MODE_SESSION_CLOSE;
460
461        case 963: /* blocklist-update */
462            return MODE_BLOCKLIST_UPDATE;
463
464        case 921: /* session-stats */
465            return MODE_SESSION_STATS;
466
467        case 'v': /* verify */
468            return MODE_TORRENT_VERIFY;
469
470        case 600: /* reannounce */
471            return MODE_TORRENT_REANNOUNCE;
472
473        case 962: /* port-test */
474            return MODE_PORT_TEST;
475
476        case 'r': /* remove */
477        case 'R': /* remove and delete */
478            return MODE_TORRENT_REMOVE;
479
480        case 960: /* move */
481            return MODE_TORRENT_SET_LOCATION;
482
483        default:
484            fprintf( stderr, "unrecognized argument %d\n", val );
485            assert( "unrecognized argument" && 0 );
486            return 0;
487    }
488}
489
490static bool debug = 0;
491static char * auth = NULL;
492static char * netrc = NULL;
493static char * sessionId = NULL;
494static bool UseSSL = false;
495
496static char*
497tr_getcwd( void )
498{
499    char * result;
500    char buf[2048];
501#ifdef WIN32
502    result = _getcwd( buf, sizeof( buf ) );
503#else
504    result = getcwd( buf, sizeof( buf ) );
505#endif
506    if( result == NULL )
507    {
508        fprintf( stderr, "getcwd error: \"%s\"", tr_strerror( errno ) );
509        *buf = '\0';
510    }
511    return tr_strdup( buf );
512}
513
514static char*
515absolutify( const char * path )
516{
517    char * buf;
518
519    if( *path == '/' )
520        buf = tr_strdup( path );
521    else {
522        char * cwd = tr_getcwd( );
523        buf = tr_buildPath( cwd, path, NULL );
524        tr_free( cwd );
525    }
526
527    return buf;
528}
529
530static char*
531getEncodedMetainfo( const char * filename )
532{
533    size_t    len = 0;
534    char *    b64 = NULL;
535    uint8_t * buf = tr_loadFile( filename, &len );
536
537    if( buf )
538    {
539        b64 = tr_base64_encode( buf, len, NULL );
540        tr_free( buf );
541    }
542    return b64;
543}
544
545static void
546addIdArg( tr_benc * args, const char * id )
547{
548    if( !*id )
549    {
550        fprintf(
551            stderr,
552            "No torrent specified!  Please use the -t option first.\n" );
553        id = "-1"; /* no torrent will have this ID, so should be a no-op */
554    }
555    if( strcmp( id, "all" ) )
556    {
557        const char * pch;
558        bool isList = strchr(id,',') || strchr(id,'-');
559        bool isNum = true;
560        for( pch=id; isNum && *pch; ++pch )
561            if( !isdigit( *pch ) )
562                isNum = false;
563        if( isNum || isList )
564            tr_rpc_parse_list_str( tr_bencDictAdd( args, "ids" ), id, strlen( id ) );
565        else
566            tr_bencDictAddStr( args, "ids", id ); /* it's a torrent sha hash */
567    }
568}
569
570static void
571addTime( tr_benc * args, const char * key, const char * arg )
572{
573    int time;
574    bool success = false;
575
576    if( arg && ( strlen( arg ) == 4 ) )
577    {
578        const char hh[3] = { arg[0], arg[1], '\0' };
579        const char mm[3] = { arg[2], arg[3], '\0' };
580        const int hour = atoi( hh );
581        const int min = atoi( mm );
582
583        if( 0<=hour && hour<24 && 0<=min && min<60 )
584        {
585            time = min + ( hour * 60 );
586            success = true;
587        }
588    }
589
590    if( success )
591        tr_bencDictAddInt( args, key, time );
592    else
593        fprintf( stderr, "Please specify the time of day in 'hhmm' format.\n" );
594}
595
596static void
597addDays( tr_benc * args, const char * key, const char * arg )
598{
599    int days = 0;
600
601    if( arg )
602    {
603        int i;
604        int valueCount;
605        int * values = tr_parseNumberRange( arg, -1, &valueCount );
606        for( i=0; i<valueCount; ++i )
607        {
608            if ( values[i] < 0 || values[i] > 7 ) continue;
609            if ( values[i] == 7 ) values[i] = 0;
610
611            days |= 1 << values[i];
612        }
613        tr_free( values );
614    }
615
616    if ( days )
617        tr_bencDictAddInt( args, key, days );
618    else
619        fprintf( stderr, "Please specify the days of the week in '1-3,4,7' format.\n" );
620}
621
622static void
623addFiles( tr_benc *    args,
624          const char * key,
625          const char * arg )
626{
627    tr_benc * files = tr_bencDictAddList( args, key, 100 );
628
629    if( !*arg )
630    {
631        fprintf( stderr, "No files specified!\n" );
632        arg = "-1"; /* no file will have this index, so should be a no-op */
633    }
634    if( strcmp( arg, "all" ) )
635    {
636        int i;
637        int valueCount;
638        int * values = tr_parseNumberRange( arg, -1, &valueCount );
639        for( i=0; i<valueCount; ++i )
640            tr_bencListAddInt( files, values[i] );
641        tr_free( values );
642    }
643}
644
645#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) )
646
647static const char * files_keys[] = {
648    "files",
649    "name",
650    "priorities",
651    "wanted"
652};
653
654static const char * details_keys[] = {
655    "activityDate",
656    "addedDate",
657    "bandwidthPriority",
658    "comment",
659    "corruptEver",
660    "creator",
661    "dateCreated",
662    "desiredAvailable",
663    "doneDate",
664    "downloadDir",
665    "downloadedEver",
666    "downloadLimit",
667    "downloadLimited",
668    "error",
669    "errorString",
670    "eta",
671    "hashString",
672    "haveUnchecked",
673    "haveValid",
674    "honorsSessionLimits",
675    "id",
676    "isFinished",
677    "isPrivate",
678    "leftUntilDone",
679    "name",
680    "peersConnected",
681    "peersGettingFromUs",
682    "peersSendingToUs",
683    "peer-limit",
684    "pieceCount",
685    "pieceSize",
686    "rateDownload",
687    "rateUpload",
688    "recheckProgress",
689    "secondsDownloading",
690    "secondsSeeding",
691    "seedRatioMode",
692    "seedRatioLimit",
693    "sizeWhenDone",
694    "startDate",
695    "status",
696    "totalSize",
697    "uploadedEver",
698    "uploadLimit",
699    "uploadLimited",
700    "webseeds",
701    "webseedsSendingToUs"
702};
703
704static const char * list_keys[] = {
705    "error",
706    "errorString",
707    "eta",
708    "id",
709    "isFinished",
710    "leftUntilDone",
711    "name",
712    "peersGettingFromUs",
713    "peersSendingToUs",
714    "rateDownload",
715    "rateUpload",
716    "sizeWhenDone",
717    "status",
718    "uploadRatio"
719};
720
721static size_t
722writeFunc( void * ptr, size_t size, size_t nmemb, void * buf )
723{
724    const size_t byteCount = size * nmemb;
725    evbuffer_add( buf, ptr, byteCount );
726    return byteCount;
727}
728
729/* look for a session id in the header in case the server gives back a 409 */
730static size_t
731parseResponseHeader( void *ptr, size_t size, size_t nmemb, void * stream UNUSED )
732{
733    const char * line = ptr;
734    const size_t line_len = size * nmemb;
735    const char * key = TR_RPC_SESSION_ID_HEADER ": ";
736    const size_t key_len = strlen( key );
737
738    if( ( line_len >= key_len ) && !memcmp( line, key, key_len ) )
739    {
740        const char * begin = line + key_len;
741        const char * end = begin;
742        while( !isspace( *end ) )
743            ++end;
744        tr_free( sessionId );
745        sessionId = tr_strndup( begin, end-begin );
746    }
747
748    return line_len;
749}
750
751static long
752getTimeoutSecs( const char * req )
753{
754  if( strstr( req, "\"method\":\"blocklist-update\"" ) != NULL )
755    return 300L;
756
757  return 60L; /* default value */
758}
759
760static char*
761getStatusString( tr_benc * t, char * buf, size_t buflen )
762{
763    int64_t status;
764    bool boolVal;
765
766    if( !tr_bencDictFindInt( t, "status", &status ) )
767    {
768        *buf = '\0';
769    }
770    else switch( status )
771    {
772        case TR_STATUS_DOWNLOAD_WAIT:
773        case TR_STATUS_SEED_WAIT:
774            tr_strlcpy( buf, "Queued", buflen );
775            break;
776
777        case TR_STATUS_STOPPED:
778            if( tr_bencDictFindBool( t, "isFinished", &boolVal ) && boolVal )
779                tr_strlcpy( buf, "Finished", buflen );
780            else
781                tr_strlcpy( buf, "Stopped", buflen );
782            break;
783
784        case TR_STATUS_CHECK_WAIT:
785        case TR_STATUS_CHECK: {
786            const char * str = status == TR_STATUS_CHECK_WAIT
787                             ? "Will Verify"
788                             : "Verifying";
789            double percent;
790            if( tr_bencDictFindReal( t, "recheckProgress", &percent ) )
791                tr_snprintf( buf, buflen, "%s (%.0f%%)", str, floor(percent*100.0) );
792            else
793                tr_strlcpy( buf, str, buflen );
794
795            break;
796        }
797
798        case TR_STATUS_DOWNLOAD:
799        case TR_STATUS_SEED: {
800            int64_t fromUs = 0;
801            int64_t toUs = 0;
802            tr_bencDictFindInt( t, "peersGettingFromUs", &fromUs );
803            tr_bencDictFindInt( t, "peersSendingToUs", &toUs );
804            if( fromUs && toUs )
805                tr_strlcpy( buf, "Up & Down", buflen );
806            else if( toUs )
807                tr_strlcpy( buf, "Downloading", buflen );
808            else if( fromUs ) {
809                int64_t leftUntilDone = 0;
810                tr_bencDictFindInt( t, "leftUntilDone", &leftUntilDone );
811                if( leftUntilDone > 0 )
812                    tr_strlcpy( buf, "Uploading", buflen );
813                else
814                    tr_strlcpy( buf, "Seeding", buflen );
815            } else {
816                tr_strlcpy( buf, "Idle", buflen );
817            }
818            break;
819        }
820
821        default:
822            tr_strlcpy( buf, "Unknown", buflen );
823            break;
824    }
825
826    return buf;
827}
828
829static const char *bandwidthPriorityNames[] =
830    { "Low", "Normal", "High", "Invalid" };
831
832static void
833printDetails( tr_benc * top )
834{
835    tr_benc *args, *torrents;
836
837    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
838      && ( tr_bencDictFindList( args, "torrents", &torrents ) ) )
839    {
840        int ti, tCount;
841        for( ti = 0, tCount = tr_bencListSize( torrents ); ti < tCount;
842             ++ti )
843        {
844            tr_benc *    t = tr_bencListChild( torrents, ti );
845            tr_benc *    l;
846            const char * str;
847            char         buf[512];
848            char         buf2[512];
849            int64_t      i, j, k;
850            bool      boolVal;
851            double       d;
852
853            printf( "NAME\n" );
854            if( tr_bencDictFindInt( t, "id", &i ) )
855                printf( "  Id: %" PRId64 "\n", i );
856            if( tr_bencDictFindStr( t, "name", &str ) )
857                printf( "  Name: %s\n", str );
858            if( tr_bencDictFindStr( t, "hashString", &str ) )
859                printf( "  Hash: %s\n", str );
860            printf( "\n" );
861
862            printf( "TRANSFER\n" );
863            getStatusString( t, buf, sizeof( buf ) );
864            printf( "  State: %s\n", buf );
865
866            if( tr_bencDictFindStr( t, "downloadDir", &str ) )
867                printf( "  Location: %s\n", str );
868
869            if( tr_bencDictFindInt( t, "sizeWhenDone", &i )
870              && tr_bencDictFindInt( t, "leftUntilDone", &j ) )
871            {
872                strlpercent( buf, 100.0 * ( i - j ) / i, sizeof( buf ) );
873                printf( "  Percent Done: %s%%\n", buf );
874            }
875
876            if( tr_bencDictFindInt( t, "eta", &i ) )
877                printf( "  ETA: %s\n", tr_strltime( buf, i, sizeof( buf ) ) );
878            if( tr_bencDictFindInt( t, "rateDownload", &i ) )
879                printf( "  Download Speed: %s\n", tr_formatter_speed_KBps( buf, i/(double)tr_speed_K, sizeof( buf ) ) );
880            if( tr_bencDictFindInt( t, "rateUpload", &i ) )
881                printf( "  Upload Speed: %s\n", tr_formatter_speed_KBps( buf, i/(double)tr_speed_K, sizeof( buf ) ) );
882            if( tr_bencDictFindInt( t, "haveUnchecked", &i )
883              && tr_bencDictFindInt( t, "haveValid", &j ) )
884            {
885                strlsize( buf, i + j, sizeof( buf ) );
886                strlsize( buf2, j, sizeof( buf2 ) );
887                printf( "  Have: %s (%s verified)\n", buf, buf2 );
888            }
889
890            if( tr_bencDictFindInt( t, "sizeWhenDone", &i ) )
891            {
892                if( i < 1 )
893                    printf( "  Availability: None\n" );
894                if( tr_bencDictFindInt( t, "desiredAvailable", &j)
895                    && tr_bencDictFindInt( t, "leftUntilDone", &k) )
896                {
897                    j += i - k;
898                    strlpercent( buf, 100.0 * j / i, sizeof( buf ) );
899                    printf( "  Availability: %s%%\n", buf );
900                }
901                if( tr_bencDictFindInt( t, "totalSize", &j ) )
902                {
903                    strlsize( buf2, i, sizeof( buf2 ) );
904                    strlsize( buf, j, sizeof( buf ) );
905                    printf( "  Total size: %s (%s wanted)\n", buf, buf2 );
906                }
907            }
908            if( tr_bencDictFindInt( t, "downloadedEver", &i )
909              && tr_bencDictFindInt( t, "uploadedEver", &j ) )
910            {
911                strlsize( buf, i, sizeof( buf ) );
912                printf( "  Downloaded: %s\n", buf );
913                strlsize( buf, j, sizeof( buf ) );
914                printf( "  Uploaded: %s\n", buf );
915                strlratio( buf, j, i, sizeof( buf ) );
916                printf( "  Ratio: %s\n", buf );
917            }
918            if( tr_bencDictFindInt( t, "corruptEver", &i ) )
919            {
920                strlsize( buf, i, sizeof( buf ) );
921                printf( "  Corrupt DL: %s\n", buf );
922            }
923            if( tr_bencDictFindStr( t, "errorString", &str ) && str && *str &&
924                tr_bencDictFindInt( t, "error", &i ) && i )
925            {
926                switch( i ) {
927                    case TR_STAT_TRACKER_WARNING: printf( "  Tracker gave a warning: %s\n", str ); break;
928                    case TR_STAT_TRACKER_ERROR:   printf( "  Tracker gave an error: %s\n", str ); break;
929                    case TR_STAT_LOCAL_ERROR:     printf( "  Error: %s\n", str ); break;
930                    default: break; /* no error */
931                }
932            }
933            if( tr_bencDictFindInt( t, "peersConnected", &i )
934              && tr_bencDictFindInt( t, "peersGettingFromUs", &j )
935              && tr_bencDictFindInt( t, "peersSendingToUs", &k ) )
936            {
937                printf(
938                    "  Peers: "
939                    "connected to %" PRId64 ", "
940                                            "uploading to %" PRId64
941                    ", "
942                    "downloading from %"
943                    PRId64 "\n",
944                    i, j, k );
945            }
946
947            if( tr_bencDictFindList( t, "webseeds", &l )
948              && tr_bencDictFindInt( t, "webseedsSendingToUs", &i ) )
949            {
950                const int64_t n = tr_bencListSize( l );
951                if( n > 0 )
952                    printf(
953                        "  Web Seeds: downloading from %" PRId64 " of %"
954                        PRId64
955                        " web seeds\n", i, n );
956            }
957            printf( "\n" );
958
959            printf( "HISTORY\n" );
960            if( tr_bencDictFindInt( t, "addedDate", &i ) && i )
961            {
962                const time_t tt = i;
963                printf( "  Date added:       %s", ctime( &tt ) );
964            }
965            if( tr_bencDictFindInt( t, "doneDate", &i ) && i )
966            {
967                const time_t tt = i;
968                printf( "  Date finished:    %s", ctime( &tt ) );
969            }
970            if( tr_bencDictFindInt( t, "startDate", &i ) && i )
971            {
972                const time_t tt = i;
973                printf( "  Date started:     %s", ctime( &tt ) );
974            }
975            if( tr_bencDictFindInt( t, "activityDate", &i ) && i )
976            {
977                const time_t tt = i;
978                printf( "  Latest activity:  %s", ctime( &tt ) );
979            }
980            if( tr_bencDictFindInt( t, "secondsDownloading", &i ) && ( i > 0 ) )
981                printf( "  Downloading Time: %s\n", tr_strltime( buf, i, sizeof( buf ) ) );
982            if( tr_bencDictFindInt( t, "secondsSeeding", &i ) && ( i > 0 ) )
983                printf( "  Seeding Time:     %s\n", tr_strltime( buf, i, sizeof( buf ) ) );
984            printf( "\n" );
985
986            printf( "ORIGINS\n" );
987            if( tr_bencDictFindInt( t, "dateCreated", &i ) && i )
988            {
989                const time_t tt = i;
990                printf( "  Date created: %s", ctime( &tt ) );
991            }
992            if( tr_bencDictFindBool( t, "isPrivate", &boolVal ) )
993                printf( "  Public torrent: %s\n", ( boolVal ? "No" : "Yes" ) );
994            if( tr_bencDictFindStr( t, "comment", &str ) && str && *str )
995                printf( "  Comment: %s\n", str );
996            if( tr_bencDictFindStr( t, "creator", &str ) && str && *str )
997                printf( "  Creator: %s\n", str );
998            if( tr_bencDictFindInt( t, "pieceCount", &i ) )
999                printf( "  Piece Count: %" PRId64 "\n", i );
1000            if( tr_bencDictFindInt( t, "pieceSize", &i ) )
1001                printf( "  Piece Size: %s\n", strlmem( buf, i, sizeof( buf ) ) );
1002            printf( "\n" );
1003
1004            printf( "LIMITS & BANDWIDTH\n" );
1005            if( tr_bencDictFindBool( t, "downloadLimited", &boolVal )
1006                && tr_bencDictFindInt( t, "downloadLimit", &i ) )
1007            {
1008                printf( "  Download Limit: " );
1009                if( boolVal )
1010                    printf( "%s\n", tr_formatter_speed_KBps( buf, i, sizeof( buf ) ) );
1011                else
1012                    printf( "Unlimited\n" );
1013            }
1014            if( tr_bencDictFindBool( t, "uploadLimited", &boolVal )
1015                && tr_bencDictFindInt( t, "uploadLimit", &i ) )
1016            {
1017                printf( "  Upload Limit: " );
1018                if( boolVal )
1019                    printf( "%s\n", tr_formatter_speed_KBps( buf, i, sizeof( buf ) ) );
1020                else
1021                    printf( "Unlimited\n" );
1022            }
1023            if( tr_bencDictFindInt( t, "seedRatioMode", &i))
1024            {
1025                switch( i ) {
1026                    case TR_RATIOLIMIT_GLOBAL:
1027                        printf( "  Ratio Limit: Default\n" );
1028                        break;
1029                    case TR_RATIOLIMIT_SINGLE:
1030                        if( tr_bencDictFindReal( t, "seedRatioLimit", &d))
1031                            printf( "  Ratio Limit: %.2f\n", d );
1032                        break;
1033                    case TR_RATIOLIMIT_UNLIMITED:
1034                        printf( "  Ratio Limit: Unlimited\n" );
1035                        break;
1036                    default: break;
1037                }
1038            }
1039            if( tr_bencDictFindBool( t, "honorsSessionLimits", &boolVal ) )
1040                printf( "  Honors Session Limits: %s\n", ( boolVal ? "Yes" : "No" ) );
1041            if( tr_bencDictFindInt ( t, "peer-limit", &i ) )
1042                printf( "  Peer limit: %" PRId64 "\n", i );
1043            if (tr_bencDictFindInt (t, "bandwidthPriority", &i))
1044                printf ("  Bandwidth Priority: %s\n",
1045                        bandwidthPriorityNames[(i + 1) & 3]);
1046
1047            printf( "\n" );
1048        }
1049    }
1050}
1051
1052static void
1053printFileList( tr_benc * top )
1054{
1055    tr_benc *args, *torrents;
1056
1057    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
1058      && ( tr_bencDictFindList( args, "torrents", &torrents ) ) )
1059    {
1060        int i, in;
1061        for( i = 0, in = tr_bencListSize( torrents ); i < in; ++i )
1062        {
1063            tr_benc *    d = tr_bencListChild( torrents, i );
1064            tr_benc *    files, *priorities, *wanteds;
1065            const char * name;
1066            if( tr_bencDictFindStr( d, "name", &name )
1067              && tr_bencDictFindList( d, "files", &files )
1068              && tr_bencDictFindList( d, "priorities", &priorities )
1069              && tr_bencDictFindList( d, "wanted", &wanteds ) )
1070            {
1071                int j = 0, jn = tr_bencListSize( files );
1072                printf( "%s (%d files):\n", name, jn );
1073                printf( "%3s  %4s %8s %3s %9s  %s\n", "#", "Done",
1074                        "Priority", "Get", "Size",
1075                        "Name" );
1076                for( j = 0, jn = tr_bencListSize( files ); j < jn; ++j )
1077                {
1078                    int64_t      have;
1079                    int64_t      length;
1080                    int64_t      priority;
1081                    int64_t      wanted;
1082                    const char * filename;
1083                    tr_benc *    file = tr_bencListChild( files, j );
1084                    if( tr_bencDictFindInt( file, "length", &length )
1085                      && tr_bencDictFindStr( file, "name", &filename )
1086                      && tr_bencDictFindInt( file, "bytesCompleted", &have )
1087                      && tr_bencGetInt( tr_bencListChild( priorities,
1088                                                          j ), &priority )
1089                      && tr_bencGetInt( tr_bencListChild( wanteds,
1090                                                          j ), &wanted ) )
1091                    {
1092                        char         sizestr[64];
1093                        double       percent = (double)have / length;
1094                        const char * pristr;
1095                        strlsize( sizestr, length, sizeof( sizestr ) );
1096                        switch( priority )
1097                        {
1098                            case TR_PRI_LOW:
1099                                pristr = "Low"; break;
1100
1101                            case TR_PRI_HIGH:
1102                                pristr = "High"; break;
1103
1104                            default:
1105                                pristr = "Normal"; break;
1106                        }
1107                        printf( "%3d: %3.0f%% %-8s %-3s %9s  %s\n",
1108                                j,
1109                                floor( 100.0 * percent ),
1110                                pristr,
1111                                ( wanted ? "Yes" : "No" ),
1112                                sizestr,
1113                                filename );
1114                    }
1115                }
1116            }
1117        }
1118    }
1119}
1120
1121static void
1122printPeersImpl( tr_benc * peers )
1123{
1124    int i, n;
1125    printf( "%-20s  %-12s  %-5s %-6s  %-6s  %s\n",
1126            "Address", "Flags", "Done", "Down", "Up", "Client" );
1127    for( i = 0, n = tr_bencListSize( peers ); i < n; ++i )
1128    {
1129        double progress;
1130        const char * address, * client, * flagstr;
1131        int64_t rateToClient, rateToPeer;
1132        tr_benc * d = tr_bencListChild( peers, i );
1133
1134        if( tr_bencDictFindStr( d, "address", &address )
1135          && tr_bencDictFindStr( d, "clientName", &client )
1136          && tr_bencDictFindReal( d, "progress", &progress )
1137          && tr_bencDictFindStr( d, "flagStr", &flagstr )
1138          && tr_bencDictFindInt( d, "rateToClient", &rateToClient )
1139          && tr_bencDictFindInt( d, "rateToPeer", &rateToPeer ) )
1140        {
1141            printf( "%-20s  %-12s  %-5.1f %6.1f  %6.1f  %s\n",
1142                    address, flagstr, (progress*100.0),
1143                    rateToClient / (double)tr_speed_K,
1144                    rateToPeer / (double)tr_speed_K,
1145                    client );
1146        }
1147    }
1148}
1149
1150static void
1151printPeers( tr_benc * top )
1152{
1153    tr_benc *args, *torrents;
1154
1155    if( tr_bencDictFindDict( top, "arguments", &args )
1156      && tr_bencDictFindList( args, "torrents", &torrents ) )
1157    {
1158        int i, n;
1159        for( i=0, n=tr_bencListSize( torrents ); i<n; ++i )
1160        {
1161            tr_benc * peers;
1162            tr_benc * torrent = tr_bencListChild( torrents, i );
1163            if( tr_bencDictFindList( torrent, "peers", &peers ) ) {
1164                printPeersImpl( peers );
1165                if( i+1<n )
1166                    printf( "\n" );
1167            }
1168        }
1169    }
1170}
1171
1172static void
1173printPiecesImpl( const uint8_t * raw, size_t rawlen, int64_t j )
1174{
1175    int i, k, len;
1176    char * str = tr_base64_decode( raw, rawlen, &len );
1177    printf( "  " );
1178    for( i=k=0; k<len; ++k ) {
1179        int e;
1180        for( e=0; i<j && e<8; ++e, ++i )
1181            printf( "%c", str[k] & (1<<(7-e)) ? '1' : '0' );
1182        printf( " " );
1183        if( !(i%64) )
1184            printf( "\n  " );
1185    }
1186    printf( "\n" );
1187    tr_free( str );
1188}
1189
1190static void
1191printPieces( tr_benc * top )
1192{
1193    tr_benc *args, *torrents;
1194
1195    if( tr_bencDictFindDict( top, "arguments", &args )
1196      && tr_bencDictFindList( args, "torrents", &torrents ) )
1197    {
1198        int i, n;
1199        for( i=0, n=tr_bencListSize( torrents ); i<n; ++i )
1200        {
1201            int64_t j;
1202            const uint8_t * raw;
1203            size_t       rawlen;
1204            tr_benc * torrent = tr_bencListChild( torrents, i );
1205            if( tr_bencDictFindRaw( torrent, "pieces", &raw, &rawlen ) &&
1206                tr_bencDictFindInt( torrent, "pieceCount", &j ) ) {
1207                printPiecesImpl( raw, rawlen, j );
1208                if( i+1<n )
1209                    printf( "\n" );
1210            }
1211        }
1212    }
1213}
1214
1215static void
1216printPortTest( tr_benc * top )
1217{
1218    tr_benc *args;
1219    if( ( tr_bencDictFindDict( top, "arguments", &args ) ) )
1220    {
1221        bool      boolVal;
1222
1223        if( tr_bencDictFindBool( args, "port-is-open", &boolVal ) )
1224            printf( "Port is open: %s\n", ( boolVal ? "Yes" : "No" ) );
1225    }
1226}
1227
1228static void
1229printTorrentList( tr_benc * top )
1230{
1231    tr_benc *args, *list;
1232
1233    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
1234      && ( tr_bencDictFindList( args, "torrents", &list ) ) )
1235    {
1236        int i, n;
1237        int64_t total_size=0;
1238        double total_up=0, total_down=0;
1239        char haveStr[32];
1240
1241        printf( "%-4s   %-4s  %9s  %-8s  %6s  %6s  %-5s  %-11s  %s\n",
1242                "ID", "Done", "Have", "ETA", "Up", "Down", "Ratio", "Status",
1243                "Name" );
1244
1245        for( i = 0, n = tr_bencListSize( list ); i < n; ++i )
1246        {
1247            int64_t      id, eta, status, up, down;
1248            int64_t      sizeWhenDone, leftUntilDone;
1249            double       ratio;
1250            const char * name;
1251            tr_benc *   d = tr_bencListChild( list, i );
1252            if( tr_bencDictFindInt( d, "eta", &eta )
1253              && tr_bencDictFindInt( d, "id", &id )
1254              && tr_bencDictFindInt( d, "leftUntilDone", &leftUntilDone )
1255              && tr_bencDictFindStr( d, "name", &name )
1256              && tr_bencDictFindInt( d, "rateDownload", &down )
1257              && tr_bencDictFindInt( d, "rateUpload", &up )
1258              && tr_bencDictFindInt( d, "sizeWhenDone", &sizeWhenDone )
1259              && tr_bencDictFindInt( d, "status", &status )
1260              && tr_bencDictFindReal( d, "uploadRatio", &ratio ) )
1261            {
1262                char etaStr[16];
1263                char statusStr[64];
1264                char ratioStr[32];
1265                char doneStr[8];
1266                int64_t error;
1267                char errorMark;
1268
1269                if( sizeWhenDone )
1270                    tr_snprintf( doneStr, sizeof( doneStr ), "%d%%", (int)( 100.0 * ( sizeWhenDone - leftUntilDone ) / sizeWhenDone ) );
1271                else
1272                    tr_strlcpy( doneStr, "n/a", sizeof( doneStr ) );
1273
1274                strlsize( haveStr, sizeWhenDone - leftUntilDone, sizeof( haveStr ) );
1275
1276                if( leftUntilDone || eta != -1 )
1277                    etaToString( etaStr, sizeof( etaStr ), eta );
1278                else
1279                    tr_snprintf( etaStr, sizeof( etaStr ), "Done" );
1280                if( tr_bencDictFindInt( d, "error", &error ) && error )
1281                    errorMark = '*';
1282                else
1283                    errorMark = ' ';
1284                printf(
1285                    "%4d%c  %4s  %9s  %-8s  %6.1f  %6.1f  %5s  %-11s  %s\n",
1286                    (int)id, errorMark,
1287                    doneStr,
1288                    haveStr,
1289                    etaStr,
1290                    up/(double)tr_speed_K,
1291                    down/(double)tr_speed_K,
1292                    strlratio2( ratioStr, ratio, sizeof( ratioStr ) ),
1293                    getStatusString( d, statusStr, sizeof( statusStr ) ),
1294                    name );
1295
1296                total_up += up;
1297                total_down += down;
1298                total_size += sizeWhenDone - leftUntilDone;
1299            }
1300        }
1301
1302        printf( "Sum:         %9s            %6.1f  %6.1f\n",
1303                strlsize( haveStr, total_size, sizeof( haveStr ) ),
1304                total_up/(double)tr_speed_K,
1305                total_down/(double)tr_speed_K );
1306    }
1307}
1308
1309static void
1310printTrackersImpl( tr_benc * trackerStats )
1311{
1312    int i;
1313    char         buf[512];
1314    tr_benc * t;
1315
1316    for( i=0; (( t = tr_bencListChild( trackerStats, i ))); ++i )
1317    {
1318        int64_t downloadCount;
1319        bool hasAnnounced;
1320        bool hasScraped;
1321        const char * host;
1322        int64_t id;
1323        bool isBackup;
1324        int64_t lastAnnouncePeerCount;
1325        const char * lastAnnounceResult;
1326        int64_t lastAnnounceStartTime;
1327        bool lastAnnounceSucceeded;
1328        int64_t lastAnnounceTime;
1329        bool lastAnnounceTimedOut;
1330        const char * lastScrapeResult;
1331        bool lastScrapeSucceeded;
1332        int64_t lastScrapeStartTime;
1333        int64_t lastScrapeTime;
1334        bool lastScrapeTimedOut;
1335        int64_t leecherCount;
1336        int64_t nextAnnounceTime;
1337        int64_t nextScrapeTime;
1338        int64_t seederCount;
1339        int64_t tier;
1340        int64_t announceState;
1341        int64_t scrapeState;
1342
1343        if( tr_bencDictFindInt ( t, "downloadCount", &downloadCount ) &&
1344            tr_bencDictFindBool( t, "hasAnnounced", &hasAnnounced ) &&
1345            tr_bencDictFindBool( t, "hasScraped", &hasScraped ) &&
1346            tr_bencDictFindStr ( t, "host", &host ) &&
1347            tr_bencDictFindInt ( t, "id", &id ) &&
1348            tr_bencDictFindBool( t, "isBackup", &isBackup ) &&
1349            tr_bencDictFindInt ( t, "announceState", &announceState ) &&
1350            tr_bencDictFindInt ( t, "scrapeState", &scrapeState ) &&
1351            tr_bencDictFindInt ( t, "lastAnnouncePeerCount", &lastAnnouncePeerCount ) &&
1352            tr_bencDictFindStr ( t, "lastAnnounceResult", &lastAnnounceResult ) &&
1353            tr_bencDictFindInt ( t, "lastAnnounceStartTime", &lastAnnounceStartTime ) &&
1354            tr_bencDictFindBool( t, "lastAnnounceSucceeded", &lastAnnounceSucceeded ) &&
1355            tr_bencDictFindInt ( t, "lastAnnounceTime", &lastAnnounceTime ) &&
1356            tr_bencDictFindBool( t, "lastAnnounceTimedOut", &lastAnnounceTimedOut ) &&
1357            tr_bencDictFindStr ( t, "lastScrapeResult", &lastScrapeResult ) &&
1358            tr_bencDictFindInt ( t, "lastScrapeStartTime", &lastScrapeStartTime ) &&
1359            tr_bencDictFindBool( t, "lastScrapeSucceeded", &lastScrapeSucceeded ) &&
1360            tr_bencDictFindInt ( t, "lastScrapeTime", &lastScrapeTime ) &&
1361            tr_bencDictFindBool( t, "lastScrapeTimedOut", &lastScrapeTimedOut ) &&
1362            tr_bencDictFindInt ( t, "leecherCount", &leecherCount ) &&
1363            tr_bencDictFindInt ( t, "nextAnnounceTime", &nextAnnounceTime ) &&
1364            tr_bencDictFindInt ( t, "nextScrapeTime", &nextScrapeTime ) &&
1365            tr_bencDictFindInt ( t, "seederCount", &seederCount ) &&
1366            tr_bencDictFindInt ( t, "tier", &tier ) )
1367        {
1368            const time_t now = time( NULL );
1369
1370            printf( "\n" );
1371            printf( "  Tracker %d: %s\n", (int)(id), host );
1372            if( isBackup )
1373                printf( "  Backup on tier %d\n", (int)tier );
1374            else
1375                printf( "  Active in tier %d\n", (int)tier );
1376
1377            if( !isBackup )
1378            {
1379                if( hasAnnounced && announceState != TR_TRACKER_INACTIVE )
1380                {
1381                    tr_strltime( buf, now - lastAnnounceTime, sizeof( buf ) );
1382                    if( lastAnnounceSucceeded )
1383                        printf( "  Got a list of %d peers %s ago\n",
1384                                (int)lastAnnouncePeerCount, buf );
1385                    else if( lastAnnounceTimedOut )
1386                        printf( "  Peer list request timed out; will retry\n" );
1387                    else
1388                        printf( "  Got an error \"%s\" %s ago\n",
1389                                lastAnnounceResult, buf );
1390                }
1391
1392                switch( announceState )
1393                {
1394                    case TR_TRACKER_INACTIVE:
1395                        printf( "  No updates scheduled\n" );
1396                        break;
1397                    case TR_TRACKER_WAITING:
1398                        tr_strltime( buf, nextAnnounceTime - now, sizeof( buf ) );
1399                        printf( "  Asking for more peers in %s\n", buf );
1400                        break;
1401                    case TR_TRACKER_QUEUED:
1402                        printf( "  Queued to ask for more peers\n" );
1403                        break;
1404                    case TR_TRACKER_ACTIVE:
1405                        tr_strltime( buf, now - lastAnnounceStartTime, sizeof( buf ) );
1406                        printf( "  Asking for more peers now... %s\n", buf );
1407                        break;
1408                }
1409
1410                if( hasScraped )
1411                {
1412                    tr_strltime( buf, now - lastScrapeTime, sizeof( buf ) );
1413                    if( lastScrapeSucceeded )
1414                        printf( "  Tracker had %d seeders and %d leechers %s ago\n",
1415                                (int)seederCount, (int)leecherCount, buf );
1416                    else if( lastScrapeTimedOut )
1417                        printf( "  Tracker scrape timed out; will retry\n" );
1418                    else
1419                        printf( "  Got a scrape error \"%s\" %s ago\n",
1420                                lastScrapeResult, buf );
1421                }
1422
1423                switch( scrapeState )
1424                {
1425                    case TR_TRACKER_INACTIVE:
1426                        break;
1427                    case TR_TRACKER_WAITING:
1428                        tr_strltime( buf, nextScrapeTime - now, sizeof( buf ) );
1429                        printf( "  Asking for peer counts in %s\n", buf );
1430                        break;
1431                    case TR_TRACKER_QUEUED:
1432                        printf( "  Queued to ask for peer counts\n" );
1433                        break;
1434                    case TR_TRACKER_ACTIVE:
1435                        tr_strltime( buf, now - lastScrapeStartTime, sizeof( buf ) );
1436                        printf( "  Asking for peer counts now... %s\n", buf );
1437                        break;
1438                }
1439            }
1440        }
1441    }
1442}
1443
1444static void
1445printTrackers( tr_benc * top )
1446{
1447    tr_benc *args, *torrents;
1448
1449    if( tr_bencDictFindDict( top, "arguments", &args )
1450      && tr_bencDictFindList( args, "torrents", &torrents ) )
1451    {
1452        int i, n;
1453        for( i=0, n=tr_bencListSize( torrents ); i<n; ++i )
1454        {
1455            tr_benc * trackerStats;
1456            tr_benc * torrent = tr_bencListChild( torrents, i );
1457            if( tr_bencDictFindList( torrent, "trackerStats", &trackerStats ) ) {
1458                printTrackersImpl( trackerStats );
1459                if( i+1<n )
1460                    printf( "\n" );
1461            }
1462        }
1463    }
1464}
1465
1466static void
1467printSession( tr_benc * top )
1468{
1469    tr_benc *args;
1470    if( ( tr_bencDictFindDict( top, "arguments", &args ) ) )
1471    {
1472        int64_t i;
1473        char buf[64];
1474        bool boolVal;
1475        const char * str;
1476
1477        printf( "VERSION\n" );
1478        if( tr_bencDictFindStr( args,  "version", &str ) )
1479            printf( "  Daemon version: %s\n", str );
1480        if( tr_bencDictFindInt( args, "rpc-version", &i ) )
1481            printf( "  RPC version: %" PRId64 "\n", i );
1482        if( tr_bencDictFindInt( args, "rpc-version-minimum", &i ) )
1483            printf( "  RPC minimum version: %" PRId64 "\n", i );
1484        printf( "\n" );
1485
1486        printf( "CONFIG\n" );
1487        if( tr_bencDictFindStr( args, "config-dir", &str ) )
1488            printf( "  Configuration directory: %s\n", str );
1489        if( tr_bencDictFindStr( args,  TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
1490            printf( "  Download directory: %s\n", str );
1491        if( tr_bencDictFindInt( args,  "download-dir-free-space", &i ) )
1492            printf( "  Download directory free space: %s\n",  strlsize( buf, i, sizeof buf ) );
1493        if( tr_bencDictFindInt( args, TR_PREFS_KEY_PEER_PORT, &i ) )
1494            printf( "  Listenport: %" PRId64 "\n", i );
1495        if( tr_bencDictFindBool( args, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) )
1496            printf( "  Portforwarding enabled: %s\n", ( boolVal ? "Yes" : "No" ) );
1497        if( tr_bencDictFindBool( args, TR_PREFS_KEY_UTP_ENABLED, &boolVal ) )
1498            printf( "  uTP enabled: %s\n", ( boolVal ? "Yes" : "No" ) );
1499        if( tr_bencDictFindBool( args, TR_PREFS_KEY_DHT_ENABLED, &boolVal ) )
1500            printf( "  Distributed hash table enabled: %s\n", ( boolVal ? "Yes" : "No" ) );
1501        if( tr_bencDictFindBool( args, TR_PREFS_KEY_LPD_ENABLED, &boolVal ) )
1502            printf( "  Local peer discovery enabled: %s\n", ( boolVal ? "Yes" : "No" ) );
1503        if( tr_bencDictFindBool( args, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) )
1504            printf( "  Peer exchange allowed: %s\n", ( boolVal ? "Yes" : "No" ) );
1505        if( tr_bencDictFindStr( args,  TR_PREFS_KEY_ENCRYPTION, &str ) )
1506            printf( "  Encryption: %s\n", str );
1507        if( tr_bencDictFindInt( args, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, &i ) )
1508            printf( "  Maximum memory cache size: %s\n", tr_formatter_mem_MB( buf, i, sizeof( buf ) ) );
1509        printf( "\n" );
1510
1511        {
1512            bool altEnabled, altTimeEnabled, upEnabled, downEnabled, seedRatioLimited;
1513            int64_t altDown, altUp, altBegin, altEnd, altDay, upLimit, downLimit, peerLimit;
1514            double seedRatioLimit;
1515
1516            if( tr_bencDictFindInt ( args, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, &altDown ) &&
1517                tr_bencDictFindBool( args, TR_PREFS_KEY_ALT_SPEED_ENABLED, &altEnabled ) &&
1518                tr_bencDictFindInt ( args, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &altBegin ) &&
1519                tr_bencDictFindBool( args, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &altTimeEnabled ) &&
1520                tr_bencDictFindInt ( args, TR_PREFS_KEY_ALT_SPEED_TIME_END, &altEnd ) &&
1521                tr_bencDictFindInt ( args, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &altDay ) &&
1522                tr_bencDictFindInt ( args, TR_PREFS_KEY_ALT_SPEED_UP_KBps, &altUp ) &&
1523                tr_bencDictFindInt ( args, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &peerLimit ) &&
1524                tr_bencDictFindInt ( args, TR_PREFS_KEY_DSPEED_KBps, &downLimit ) &&
1525                tr_bencDictFindBool( args, TR_PREFS_KEY_DSPEED_ENABLED, &downEnabled ) &&
1526                tr_bencDictFindInt ( args, TR_PREFS_KEY_USPEED_KBps, &upLimit ) &&
1527                tr_bencDictFindBool( args, TR_PREFS_KEY_USPEED_ENABLED, &upEnabled ) &&
1528                tr_bencDictFindReal( args, "seedRatioLimit", &seedRatioLimit ) &&
1529                tr_bencDictFindBool( args, "seedRatioLimited", &seedRatioLimited) )
1530            {
1531                char buf[128];
1532                char buf2[128];
1533                char buf3[128];
1534
1535                printf( "LIMITS\n" );
1536                printf( "  Peer limit: %" PRId64 "\n", peerLimit );
1537
1538                if( seedRatioLimited )
1539                    tr_snprintf( buf, sizeof( buf ), "%.2f", seedRatioLimit );
1540                else
1541                    tr_strlcpy( buf, "Unlimited", sizeof( buf ) );
1542                printf( "  Default seed ratio limit: %s\n", buf );
1543
1544                if( altEnabled )
1545                    tr_formatter_speed_KBps( buf, altUp, sizeof( buf ) );
1546                else if( upEnabled )
1547                    tr_formatter_speed_KBps( buf, upLimit, sizeof( buf ) );
1548                else
1549                    tr_strlcpy( buf, "Unlimited", sizeof( buf ) );
1550                printf( "  Upload speed limit: %s  (%s limit: %s; %s turtle limit: %s)\n",
1551                        buf,
1552                        upEnabled ? "Enabled" : "Disabled",
1553                        tr_formatter_speed_KBps( buf2, upLimit, sizeof( buf2 ) ),
1554                        altEnabled ? "Enabled" : "Disabled",
1555                        tr_formatter_speed_KBps( buf3, altUp, sizeof( buf3 ) ) );
1556
1557                if( altEnabled )
1558                    tr_formatter_speed_KBps( buf, altDown, sizeof( buf ) );
1559                else if( downEnabled )
1560                    tr_formatter_speed_KBps( buf, downLimit, sizeof( buf ) );
1561                else
1562                    tr_strlcpy( buf, "Unlimited", sizeof( buf ) );
1563                printf( "  Download speed limit: %s  (%s limit: %s; %s turtle limit: %s)\n",
1564                        buf,
1565                        downEnabled ? "Enabled" : "Disabled",
1566                        tr_formatter_speed_KBps( buf2, downLimit, sizeof( buf2 ) ),
1567                        altEnabled ? "Enabled" : "Disabled",
1568                        tr_formatter_speed_KBps( buf3, altDown, sizeof( buf3 ) ) );
1569
1570                if( altTimeEnabled ) {
1571                    printf( "  Turtle schedule: %02d:%02d - %02d:%02d  ",
1572                            (int)(altBegin/60), (int)(altBegin%60),
1573                            (int)(altEnd/60), (int)(altEnd%60) );
1574                    if( altDay & TR_SCHED_SUN )   printf( "Sun " );
1575                    if( altDay & TR_SCHED_MON )   printf( "Mon " );
1576                    if( altDay & TR_SCHED_TUES )  printf( "Tue " );
1577                    if( altDay & TR_SCHED_WED )   printf( "Wed " );
1578                    if( altDay & TR_SCHED_THURS ) printf( "Thu " );
1579                    if( altDay & TR_SCHED_FRI )   printf( "Fri " );
1580                    if( altDay & TR_SCHED_SAT )   printf( "Sat " );
1581                    printf( "\n" );
1582                }
1583            }
1584        }
1585        printf( "\n" );
1586
1587        printf( "MISC\n" );
1588        if( tr_bencDictFindBool( args, TR_PREFS_KEY_START, &boolVal ) )
1589            printf( "  Autostart added torrents: %s\n", ( boolVal ? "Yes" : "No" ) );
1590        if( tr_bencDictFindBool( args, TR_PREFS_KEY_TRASH_ORIGINAL, &boolVal ) )
1591            printf( "  Delete automatically added torrents: %s\n", ( boolVal ? "Yes" : "No" ) );
1592    }
1593}
1594
1595static void
1596printSessionStats( tr_benc * top )
1597{
1598    tr_benc *args, *d;
1599    if( ( tr_bencDictFindDict( top, "arguments", &args ) ) )
1600    {
1601        char buf[512];
1602        int64_t up, down, secs, sessions;
1603
1604        if( tr_bencDictFindDict( args, "current-stats", &d )
1605            && tr_bencDictFindInt( d, "uploadedBytes", &up )
1606            && tr_bencDictFindInt( d, "downloadedBytes", &down )
1607            && tr_bencDictFindInt( d, "secondsActive", &secs ) )
1608        {
1609            printf( "\nCURRENT SESSION\n" );
1610            printf( "  Uploaded:   %s\n", strlsize( buf, up, sizeof( buf ) ) );
1611            printf( "  Downloaded: %s\n", strlsize( buf, down, sizeof( buf ) ) );
1612            printf( "  Ratio:      %s\n", strlratio( buf, up, down, sizeof( buf ) ) );
1613            printf( "  Duration:   %s\n", tr_strltime( buf, secs, sizeof( buf ) ) );
1614        }
1615
1616        if( tr_bencDictFindDict( args, "cumulative-stats", &d )
1617            && tr_bencDictFindInt( d, "sessionCount", &sessions )
1618            && tr_bencDictFindInt( d, "uploadedBytes", &up )
1619            && tr_bencDictFindInt( d, "downloadedBytes", &down )
1620            && tr_bencDictFindInt( d, "secondsActive", &secs ) )
1621        {
1622            printf( "\nTOTAL\n" );
1623            printf( "  Started %lu times\n", (unsigned long)sessions );
1624            printf( "  Uploaded:   %s\n", strlsize( buf, up, sizeof( buf ) ) );
1625            printf( "  Downloaded: %s\n", strlsize( buf, down, sizeof( buf ) ) );
1626            printf( "  Ratio:      %s\n", strlratio( buf, up, down, sizeof( buf ) ) );
1627            printf( "  Duration:   %s\n", tr_strltime( buf, secs, sizeof( buf ) ) );
1628        }
1629    }
1630}
1631
1632static char id[4096];
1633
1634static int
1635processResponse( const char * rpcurl, const void * response, size_t len )
1636{
1637    tr_benc top;
1638    int status = EXIT_SUCCESS;
1639
1640    if( debug )
1641        fprintf( stderr, "got response (len %d):\n--------\n%*.*s\n--------\n",
1642                 (int)len, (int)len, (int)len, (const char*) response );
1643
1644    if( tr_jsonParse( NULL, response, len, &top, NULL ) )
1645    {
1646        tr_nerr( MY_NAME, "Unable to parse response \"%*.*s\"", (int)len,
1647                 (int)len, (char*)response );
1648        status |= EXIT_FAILURE;
1649    }
1650    else
1651    {
1652        int64_t      tag = -1;
1653        const char * str;
1654
1655        if(tr_bencDictFindStr(&top, "result", &str))
1656        {
1657            if( strcmp( str, "success") )
1658            {
1659                printf( "Error: %s\n", str );
1660                status |= EXIT_FAILURE;
1661            }
1662            else
1663            {
1664        tr_bencDictFindInt( &top, "tag", &tag );
1665
1666        switch( tag )
1667        {
1668            case TAG_SESSION:
1669                printSession( &top ); break;
1670
1671            case TAG_STATS:
1672                printSessionStats( &top ); break;
1673
1674            case TAG_DETAILS:
1675                printDetails( &top ); break;
1676
1677            case TAG_FILES:
1678                printFileList( &top ); break;
1679
1680            case TAG_LIST:
1681                printTorrentList( &top ); break;
1682
1683            case TAG_PEERS:
1684                printPeers( &top ); break;
1685
1686            case TAG_PIECES:
1687                printPieces( &top ); break;
1688
1689            case TAG_PORTTEST:
1690                printPortTest( &top ); break;
1691
1692            case TAG_TRACKERS:
1693                printTrackers( &top ); break;
1694
1695            case TAG_TORRENT_ADD: {
1696                int64_t i;
1697                tr_benc * b = &top;
1698                if( tr_bencDictFindDict( &top, ARGUMENTS, &b )
1699                        && tr_bencDictFindDict( b, "torrent-added", &b )
1700                        && tr_bencDictFindInt( b, "id", &i ) )
1701                    tr_snprintf( id, sizeof(id), "%"PRId64, i );
1702                /* fall-through to default: to give success or failure msg */
1703            }
1704            default:
1705                if( !tr_bencDictFindStr( &top, "result", &str ) )
1706                    status |= EXIT_FAILURE;
1707                else {
1708                    printf( "%s responded: \"%s\"\n", rpcurl, str );
1709                    if( strcmp( str, "success") )
1710                        status |= EXIT_FAILURE;
1711                }
1712        }
1713
1714        tr_bencFree( &top );
1715    }
1716        }
1717        else
1718            status |= EXIT_FAILURE;
1719    }
1720
1721    return status;
1722}
1723
1724static CURL*
1725tr_curl_easy_init( struct evbuffer * writebuf )
1726{
1727    CURL * curl = curl_easy_init( );
1728    curl_easy_setopt( curl, CURLOPT_USERAGENT, MY_NAME "/" LONG_VERSION_STRING );
1729    curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, writeFunc );
1730    curl_easy_setopt( curl, CURLOPT_WRITEDATA, writebuf );
1731    curl_easy_setopt( curl, CURLOPT_HEADERFUNCTION, parseResponseHeader );
1732    curl_easy_setopt( curl, CURLOPT_POST, 1 );
1733    curl_easy_setopt( curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL );
1734    curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
1735    curl_easy_setopt( curl, CURLOPT_VERBOSE, debug );
1736    curl_easy_setopt( curl, CURLOPT_ENCODING, "" ); /* "" tells curl to fill in the blanks with what it was compiled to support */
1737    if( netrc )
1738        curl_easy_setopt( curl, CURLOPT_NETRC_FILE, netrc );
1739    if( auth )
1740        curl_easy_setopt( curl, CURLOPT_USERPWD, auth );
1741    if( UseSSL )
1742        curl_easy_setopt( curl, CURLOPT_SSL_VERIFYPEER, 0 ); /* since most certs will be self-signed, do not verify against CA */               
1743    if( sessionId ) {
1744        char * h = tr_strdup_printf( "%s: %s", TR_RPC_SESSION_ID_HEADER, sessionId );
1745        struct curl_slist * custom_headers = curl_slist_append( NULL, h );
1746        curl_easy_setopt( curl, CURLOPT_HTTPHEADER, custom_headers );
1747        /* fixme: leaks */
1748    }
1749    return curl;
1750}
1751
1752static int
1753flush( const char * rpcurl, tr_benc ** benc )
1754{
1755    CURLcode res;
1756    CURL * curl;
1757    int status = EXIT_SUCCESS;
1758    struct evbuffer * buf = evbuffer_new( );
1759    char * json = tr_bencToStr( *benc, TR_FMT_JSON_LEAN, NULL );
1760    char *rpcurl_http =  tr_strdup_printf( UseSSL? "https://%s" : "http://%s", rpcurl );
1761
1762    curl = tr_curl_easy_init( buf );
1763    curl_easy_setopt( curl, CURLOPT_URL, rpcurl_http );
1764    curl_easy_setopt( curl, CURLOPT_POSTFIELDS, json );
1765    curl_easy_setopt( curl, CURLOPT_TIMEOUT, getTimeoutSecs( json ) );
1766
1767    if( debug )
1768        fprintf( stderr, "posting:\n--------\n%s\n--------\n", json );
1769
1770    if(( res = curl_easy_perform( curl )))
1771    {
1772        tr_nerr( MY_NAME, "(%s) %s", rpcurl_http, curl_easy_strerror( res ) );
1773        status |= EXIT_FAILURE;
1774    }
1775    else
1776    {
1777        long response;
1778        curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &response );
1779        switch( response ) {
1780            case 200:
1781                status |= processResponse( rpcurl, (const char*) evbuffer_pullup( buf, -1 ), evbuffer_get_length( buf ) );
1782                break;
1783            case 409:
1784                /* Session id failed. Our curl header func has already
1785                 * pulled the new session id from this response's headers,
1786                 * build a new CURL* and try again */
1787                curl_easy_cleanup( curl );
1788                curl = NULL;
1789                status |= flush( rpcurl, benc );
1790                benc = NULL;
1791                break;
1792            default:
1793                fprintf( stderr, "Unexpected response: %s\n", evbuffer_pullup( buf, -1 ) );
1794                status |= EXIT_FAILURE;
1795                break;
1796        }
1797    }
1798
1799    /* cleanup */
1800    tr_free( rpcurl_http );
1801    tr_free( json );
1802    evbuffer_free( buf );
1803    if( curl != 0 )
1804        curl_easy_cleanup( curl );
1805    if( benc != NULL ) {
1806        tr_bencFree( *benc );
1807        *benc = 0;
1808    }
1809    return status;
1810}
1811
1812static tr_benc*
1813ensure_sset( tr_benc ** sset )
1814{
1815    tr_benc * args;
1816
1817    if( *sset )
1818        args = tr_bencDictFind( *sset, ARGUMENTS );
1819    else {
1820        *sset = tr_new0( tr_benc, 1 );
1821        tr_bencInitDict( *sset, 3 );
1822        tr_bencDictAddStr( *sset, "method", "session-set" );
1823        args = tr_bencDictAddDict( *sset, ARGUMENTS, 0 );
1824    }
1825
1826    return args;
1827}
1828
1829static tr_benc*
1830ensure_tset( tr_benc ** tset )
1831{
1832    tr_benc * args;
1833
1834    if( *tset )
1835        args = tr_bencDictFind( *tset, ARGUMENTS );
1836    else {
1837        *tset = tr_new0( tr_benc, 1 );
1838        tr_bencInitDict( *tset, 3 );
1839        tr_bencDictAddStr( *tset, "method", "torrent-set" );
1840        args = tr_bencDictAddDict( *tset, ARGUMENTS, 1 );
1841    }
1842
1843    return args;
1844}
1845
1846static int
1847processArgs( const char * rpcurl, int argc, const char ** argv )
1848{
1849    int c;
1850    int status = EXIT_SUCCESS;
1851    const char * optarg;
1852    tr_benc *sset = 0;
1853    tr_benc *tset = 0;
1854    tr_benc *tadd = 0;
1855
1856    *id = '\0';
1857
1858    while(( c = tr_getopt( getUsage( ), argc, argv, opts, &optarg )))
1859    {
1860        const int stepMode = getOptMode( c );
1861
1862        if( !stepMode ) /* meta commands */
1863        {
1864            switch( c )
1865            {
1866                case 'a': /* add torrent */
1867                    if( sset != 0 ) status |= flush( rpcurl, &sset );
1868                    if( tadd != 0 ) status |= flush( rpcurl, &tadd );
1869                    if( tset != 0 ) { addIdArg( tr_bencDictFind( tset, ARGUMENTS ), id ); status |= flush( rpcurl, &tset ); }
1870                    tadd = tr_new0( tr_benc, 1 );
1871                    tr_bencInitDict( tadd, 3 );
1872                    tr_bencDictAddStr( tadd, "method", "torrent-add" );
1873                    tr_bencDictAddInt( tadd, "tag", TAG_TORRENT_ADD );
1874                    tr_bencDictAddDict( tadd, ARGUMENTS, 0 );
1875                    break;
1876
1877                case 'b': /* debug */
1878                    debug = true;
1879                    break;
1880
1881                case 'n': /* auth */
1882                    auth = tr_strdup( optarg );
1883                    break;
1884
1885                case 810: /* authenv */
1886                    {
1887                        char *authenv = getenv("TR_AUTH");
1888                        if( !authenv ) {
1889                            fprintf( stderr, "The TR_AUTH environment variable is not set\n" );
1890                            exit( 0 );
1891                        }
1892                        auth = tr_strdup( authenv );
1893                    }
1894                    break;
1895
1896                case 'N': /* netrc */
1897                    netrc = tr_strdup( optarg );
1898                    break;
1899
1900                case 820: /* UseSSL */
1901                    UseSSL = true;
1902                    break;
1903
1904                case 't': /* set current torrent */
1905                    if( tadd != 0 ) status |= flush( rpcurl, &tadd );
1906                    if( tset != 0 ) { addIdArg( tr_bencDictFind( tset, ARGUMENTS ), id ); status |= flush( rpcurl, &tset ); }
1907                    tr_strlcpy( id, optarg, sizeof( id ) );
1908                    break;
1909
1910                case 'V': /* show version number */
1911                    fprintf( stderr, "%s %s\n", MY_NAME, LONG_VERSION_STRING );
1912                    exit( 0 );
1913                    break;
1914
1915                case TR_OPT_ERR:
1916                    fprintf( stderr, "invalid option\n" );
1917                    showUsage( );
1918                    status |= EXIT_FAILURE;
1919                    break;
1920
1921                case TR_OPT_UNK:
1922                    if( tadd ) {
1923                        tr_benc * args = tr_bencDictFind( tadd, ARGUMENTS );
1924                        char * tmp = getEncodedMetainfo( optarg );
1925                        if( tmp )
1926                            tr_bencDictAddStr( args, "metainfo", tmp );
1927                        else
1928                            tr_bencDictAddStr( args, "filename", optarg );
1929                        tr_free( tmp );
1930                    } else {
1931                        fprintf( stderr, "Unknown option: %s\n", optarg );
1932                        status |= EXIT_FAILURE;
1933                    }
1934                    break;
1935            }
1936        }
1937        else if( stepMode == MODE_TORRENT_GET )
1938        {
1939            size_t i, n;
1940            tr_benc * top = tr_new0( tr_benc, 1 );
1941            tr_benc * args;
1942            tr_benc * fields;
1943            tr_bencInitDict( top, 3 );
1944            tr_bencDictAddStr( top, "method", "torrent-get" );
1945            args = tr_bencDictAddDict( top, ARGUMENTS, 0 );
1946            fields = tr_bencDictAddList( args, "fields", 0 );
1947
1948            if( tset != 0 ) { addIdArg( tr_bencDictFind( tset, ARGUMENTS ), id ); status |= flush( rpcurl, &tset ); }
1949
1950            switch( c )
1951            {
1952                case 'i': tr_bencDictAddInt( top, "tag", TAG_DETAILS );
1953                          n = TR_N_ELEMENTS( details_keys );
1954                          for( i=0; i<n; ++i ) tr_bencListAddStr( fields, details_keys[i] );
1955                          addIdArg( args, id );
1956                          break;
1957                case 'l': tr_bencDictAddInt( top, "tag", TAG_LIST );
1958                          n = TR_N_ELEMENTS( list_keys );
1959                          for( i=0; i<n; ++i ) tr_bencListAddStr( fields, list_keys[i] );
1960                          break;
1961                case 940: tr_bencDictAddInt( top, "tag", TAG_FILES );
1962                          n = TR_N_ELEMENTS( files_keys );
1963                          for( i=0; i<n; ++i ) tr_bencListAddStr( fields, files_keys[i] );
1964                          addIdArg( args, id );
1965                          break;
1966                case 941: tr_bencDictAddInt( top, "tag", TAG_PEERS );
1967                          tr_bencListAddStr( fields, "peers" );
1968                          addIdArg( args, id );
1969                          break;
1970                case 942: tr_bencDictAddInt( top, "tag", TAG_PIECES );
1971                          tr_bencListAddStr( fields, "pieces" );
1972                          tr_bencListAddStr( fields, "pieceCount" );
1973                          addIdArg( args, id );
1974                          break;
1975                case 943: tr_bencDictAddInt( top, "tag", TAG_TRACKERS );
1976                          tr_bencListAddStr( fields, "trackerStats" );
1977                          addIdArg( args, id );
1978                          break;
1979                default:  assert( "unhandled value" && 0 );
1980            }
1981
1982            status |= flush( rpcurl, &top );
1983        }
1984        else if( stepMode == MODE_SESSION_SET )
1985        {
1986            tr_benc * args = ensure_sset( &sset );
1987
1988            switch( c )
1989            {
1990                case 800: tr_bencDictAddStr( args, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, optarg );
1991                          tr_bencDictAddBool( args, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, true );
1992                          break;
1993                case 801: tr_bencDictAddBool( args, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, false );
1994                          break;
1995                case 970: tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_ENABLED, true );
1996                          break;
1997                case 971: tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_ENABLED, false );
1998                          break;
1999                case 972: tr_bencDictAddInt( args, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, numarg( optarg ) );
2000                          break;
2001                case 973: tr_bencDictAddInt( args, TR_PREFS_KEY_ALT_SPEED_UP_KBps, numarg( optarg ) );
2002                          break;
2003                case 974: tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, true );
2004                          break;
2005                case 975: tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, false );
2006                          break;
2007                case 976: addTime( args, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, optarg );
2008                          break;
2009                case 977: addTime( args, TR_PREFS_KEY_ALT_SPEED_TIME_END, optarg );
2010                          break;
2011                case 978: addDays( args, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, optarg );
2012                          break;
2013                case 'c': tr_bencDictAddStr( args, TR_PREFS_KEY_INCOMPLETE_DIR, optarg );
2014                          tr_bencDictAddBool( args, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, true );
2015                          break;
2016                case 'C': tr_bencDictAddBool( args, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, false );
2017                          break;
2018                case 'e': tr_bencDictAddInt( args, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, atoi(optarg) );
2019                          break;
2020                case 910: tr_bencDictAddStr( args, TR_PREFS_KEY_ENCRYPTION, "required" );
2021                          break;
2022                case 911: tr_bencDictAddStr( args, TR_PREFS_KEY_ENCRYPTION, "preferred" );
2023                          break;
2024                case 912: tr_bencDictAddStr( args, TR_PREFS_KEY_ENCRYPTION, "tolerated" );
2025                          break;
2026                case 'm': tr_bencDictAddBool( args, TR_PREFS_KEY_PORT_FORWARDING, true );
2027                          break;
2028                case 'M': tr_bencDictAddBool( args, TR_PREFS_KEY_PORT_FORWARDING, false );
2029                          break;
2030                case 'o': tr_bencDictAddBool( args, TR_PREFS_KEY_DHT_ENABLED, true );
2031                          break;
2032                case 'O': tr_bencDictAddBool( args, TR_PREFS_KEY_DHT_ENABLED, false );
2033                          break;
2034                case 830: tr_bencDictAddBool( args, TR_PREFS_KEY_UTP_ENABLED, true );
2035                          break;
2036                case 831: tr_bencDictAddBool( args, TR_PREFS_KEY_UTP_ENABLED, false );
2037                          break;
2038                case 'p': tr_bencDictAddInt( args, TR_PREFS_KEY_PEER_PORT, numarg( optarg ) );
2039                          break;
2040                case 'P': tr_bencDictAddBool( args, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, true);
2041                          break;
2042                case 'x': tr_bencDictAddBool( args, TR_PREFS_KEY_PEX_ENABLED, true );
2043                          break;
2044                case 'X': tr_bencDictAddBool( args, TR_PREFS_KEY_PEX_ENABLED, false );
2045                          break;
2046                case 'y': tr_bencDictAddBool( args, TR_PREFS_KEY_LPD_ENABLED, true );
2047                          break;
2048                case 'Y': tr_bencDictAddBool( args, TR_PREFS_KEY_LPD_ENABLED, false );
2049                          break;
2050                case 953: tr_bencDictAddReal( args, "seedRatioLimit", atof(optarg) );
2051                          tr_bencDictAddBool( args, "seedRatioLimited", true );
2052                          break;
2053                case 954: tr_bencDictAddBool( args, "seedRatioLimited", false );
2054                          break;
2055                case 990: tr_bencDictAddBool( args, TR_PREFS_KEY_START, false );
2056                          break;
2057                case 991: tr_bencDictAddBool( args, TR_PREFS_KEY_START, true );
2058                          break;
2059                case 992: tr_bencDictAddBool( args, TR_PREFS_KEY_TRASH_ORIGINAL, true );
2060                          break;
2061                case 993: tr_bencDictAddBool( args, TR_PREFS_KEY_TRASH_ORIGINAL, false );
2062                          break;
2063                default:  assert( "unhandled value" && 0 );
2064                          break;
2065            }
2066        }
2067        else if( stepMode == ( MODE_SESSION_SET | MODE_TORRENT_SET ) )
2068        {
2069            tr_benc * targs = 0;
2070            tr_benc * sargs = 0;
2071
2072            if( *id )
2073                targs = ensure_tset( &tset );
2074            else
2075                sargs = ensure_sset( &sset );
2076
2077            switch( c )
2078            {
2079                case 'd': if( targs ) {
2080                              tr_bencDictAddInt( targs, "downloadLimit", numarg( optarg ) );
2081                              tr_bencDictAddBool( targs, "downloadLimited", true );
2082                          } else {
2083                              tr_bencDictAddInt( sargs, TR_PREFS_KEY_DSPEED_KBps, numarg( optarg ) );
2084                              tr_bencDictAddBool( sargs, TR_PREFS_KEY_DSPEED_ENABLED, true );
2085                          }
2086                          break;
2087                case 'D': if( targs )
2088                              tr_bencDictAddBool( targs, "downloadLimited", false );
2089                          else
2090                              tr_bencDictAddBool( sargs, TR_PREFS_KEY_DSPEED_ENABLED, false );
2091                          break;
2092                case 'u': if( targs ) {
2093                              tr_bencDictAddInt( targs, "uploadLimit", numarg( optarg ) );
2094                              tr_bencDictAddBool( targs, "uploadLimited", true );
2095                          } else {
2096                              tr_bencDictAddInt( sargs, TR_PREFS_KEY_USPEED_KBps, numarg( optarg ) );
2097                              tr_bencDictAddBool( sargs, TR_PREFS_KEY_USPEED_ENABLED, true );
2098                          }
2099                          break;
2100                case 'U': if( targs )
2101                              tr_bencDictAddBool( targs, "uploadLimited", false );
2102                          else
2103                              tr_bencDictAddBool( sargs, TR_PREFS_KEY_USPEED_ENABLED, false );
2104                          break;
2105                case 930: if( targs )
2106                              tr_bencDictAddInt( targs, "peer-limit", atoi(optarg) );
2107                          else
2108                              tr_bencDictAddInt( sargs, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, atoi(optarg) );
2109                          break;
2110                default:  assert( "unhandled value" && 0 );
2111                          break;
2112            }
2113        }
2114        else if( stepMode == MODE_TORRENT_SET )
2115        {
2116            tr_benc * args = ensure_tset( &tset );
2117
2118            switch( c )
2119            {
2120                case 712: tr_bencListAddInt( tr_bencDictAddList( args, "trackerRemove", 1 ), atoi( optarg ) );
2121                          break;
2122                case 950: tr_bencDictAddReal( args, "seedRatioLimit", atof(optarg) );
2123                          tr_bencDictAddInt( args, "seedRatioMode", TR_RATIOLIMIT_SINGLE );
2124                          break;
2125                case 951: tr_bencDictAddInt( args, "seedRatioMode", TR_RATIOLIMIT_GLOBAL );
2126                          break;
2127                case 952: tr_bencDictAddInt( args, "seedRatioMode", TR_RATIOLIMIT_UNLIMITED );
2128                          break;
2129                case 984: tr_bencDictAddBool( args, "honorsSessionLimits", true );
2130                          break;
2131                case 985: tr_bencDictAddBool( args, "honorsSessionLimits", false );
2132                          break;
2133                default:  assert( "unhandled value" && 0 );
2134                          break;
2135            }
2136        }
2137        else if( stepMode == ( MODE_TORRENT_SET | MODE_TORRENT_ADD ) )
2138        {
2139            tr_benc * args;
2140
2141            if( tadd )
2142                args = tr_bencDictFind( tadd, ARGUMENTS );
2143            else
2144                args = ensure_tset( &tset );
2145
2146            switch( c )
2147            {
2148                case 'g': addFiles( args, "files-wanted", optarg );
2149                          break;
2150                case 'G': addFiles( args, "files-unwanted", optarg );
2151                          break;
2152                case 900: addFiles( args, "priority-high", optarg );
2153                          break;
2154                case 901: addFiles( args, "priority-normal", optarg );
2155                          break;
2156                case 902: addFiles( args, "priority-low", optarg );
2157                          break;
2158                case 700: tr_bencDictAddInt( args, "bandwidthPriority",  1 );
2159                          break;
2160                case 701: tr_bencDictAddInt( args, "bandwidthPriority",  0 );
2161                          break;
2162                case 702: tr_bencDictAddInt( args, "bandwidthPriority", -1 );
2163                          break;
2164                case 710: tr_bencListAddStr( tr_bencDictAddList( args, "trackerAdd", 1 ), optarg );
2165                          break;
2166                default:  assert( "unhandled value" && 0 );
2167                          break;
2168            }
2169        }
2170        else if( c == 961 ) /* set location */
2171        {
2172            if( tadd )
2173            {
2174                tr_benc * args = tr_bencDictFind( tadd, ARGUMENTS );
2175                tr_bencDictAddStr( args, "download-dir", optarg );
2176            }
2177            else
2178            {
2179                tr_benc * args;
2180                tr_benc * top = tr_new0( tr_benc, 1 );
2181                tr_bencInitDict( top, 2 );
2182                tr_bencDictAddStr( top, "method", "torrent-set-location" );
2183                args = tr_bencDictAddDict( top, ARGUMENTS, 3 );
2184                tr_bencDictAddStr( args, "location", optarg );
2185                tr_bencDictAddBool( args, "move", false );
2186                addIdArg( args, id );
2187                status |= flush( rpcurl, &top );
2188                break;
2189            }
2190        }
2191        else switch( c )
2192        {
2193            case 920: /* session-info */
2194            {
2195                tr_benc * top = tr_new0( tr_benc, 1 );
2196                tr_bencInitDict( top, 2 );
2197                tr_bencDictAddStr( top, "method", "session-get" );
2198                tr_bencDictAddInt( top, "tag", TAG_SESSION );
2199                status |= flush( rpcurl, &top );
2200                break;
2201            }
2202            case 's': /* start */
2203            {
2204                if( tadd )
2205                    tr_bencDictAddBool( tr_bencDictFind( tadd, "arguments" ), "paused", false );
2206                else {
2207                    tr_benc * top = tr_new0( tr_benc, 1 );
2208                    tr_bencInitDict( top, 2 );
2209                    tr_bencDictAddStr( top, "method", "torrent-start" );
2210                    addIdArg( tr_bencDictAddDict( top, ARGUMENTS, 1 ), id );
2211                    status |= flush( rpcurl, &top );
2212                }
2213                break;
2214            }
2215            case 'S': /* stop */
2216            {
2217                if( tadd )
2218                    tr_bencDictAddBool( tr_bencDictFind( tadd, "arguments" ), "paused", true );
2219                else {
2220                    tr_benc * top = tr_new0( tr_benc, 1 );
2221                    tr_bencInitDict( top, 2 );
2222                    tr_bencDictAddStr( top, "method", "torrent-stop" );
2223                    addIdArg( tr_bencDictAddDict( top, ARGUMENTS, 1 ), id );
2224                    status |= flush( rpcurl, &top );
2225                }
2226                break;
2227            }
2228            case 'w':
2229            {
2230                char * path = absolutify( optarg );
2231                if( tadd )
2232                    tr_bencDictAddStr( tr_bencDictFind( tadd, "arguments" ), "download-dir", path );
2233                else {
2234                    tr_benc * args = ensure_sset( &sset );
2235                    tr_bencDictAddStr( args, "download-dir", path );
2236                }
2237                tr_free( path );
2238                break;
2239            }
2240            case 850:
2241            {
2242                tr_benc * top = tr_new0( tr_benc, 1 );
2243                tr_bencInitDict( top, 1 );
2244                tr_bencDictAddStr( top, "method", "session-close" );
2245                status |= flush( rpcurl, &top );
2246                break;
2247            }
2248            case 963:
2249            {
2250                tr_benc * top = tr_new0( tr_benc, 1 );
2251                tr_bencInitDict( top, 1 );
2252                tr_bencDictAddStr( top, "method", "blocklist-update" );
2253                status |= flush( rpcurl, &top );
2254                break;
2255            }
2256            case 921:
2257            {
2258                tr_benc * top = tr_new0( tr_benc, 1 );
2259                tr_bencInitDict( top, 2 );
2260                tr_bencDictAddStr( top, "method", "session-stats" );
2261                tr_bencDictAddInt( top, "tag", TAG_STATS );
2262                status |= flush( rpcurl, &top );
2263                break;
2264            }
2265            case 962:
2266            {
2267                tr_benc * top = tr_new0( tr_benc, 1 );
2268                tr_bencInitDict( top, 2 );
2269                tr_bencDictAddStr( top, "method", "port-test" );
2270                tr_bencDictAddInt( top, "tag", TAG_PORTTEST );
2271                status |= flush( rpcurl, &top );
2272                break;
2273            }
2274            case 600:
2275            {
2276                tr_benc * top;
2277                if( tset != 0 ) { addIdArg( tr_bencDictFind( tset, ARGUMENTS ), id ); status |= flush( rpcurl, &tset ); }
2278                top = tr_new0( tr_benc, 1 );
2279                tr_bencInitDict( top, 2 );
2280                tr_bencDictAddStr( top, "method", "torrent-reannounce" );
2281                addIdArg( tr_bencDictAddDict( top, ARGUMENTS, 1 ), id );
2282                status |= flush( rpcurl, &top );
2283                break;
2284            }
2285            case 'v':
2286            {
2287                tr_benc * top;
2288                if( tset != 0 ) { addIdArg( tr_bencDictFind( tset, ARGUMENTS ), id ); status |= flush( rpcurl, &tset ); }
2289                top = tr_new0( tr_benc, 1 );
2290                tr_bencInitDict( top, 2 );
2291                tr_bencDictAddStr( top, "method", "torrent-verify" );
2292                addIdArg( tr_bencDictAddDict( top, ARGUMENTS, 1 ), id );
2293                status |= flush( rpcurl, &top );
2294                break;
2295            }
2296            case 'r':
2297            case 'R':
2298            {
2299                tr_benc * args;
2300                tr_benc * top = tr_new0( tr_benc, 1 );
2301                tr_bencInitDict( top, 2 );
2302                tr_bencDictAddStr( top, "method", "torrent-remove" );
2303                args = tr_bencDictAddDict( top, ARGUMENTS, 2 );
2304                tr_bencDictAddBool( args, "delete-local-data", c=='R' );
2305                addIdArg( args, id );
2306                status |= flush( rpcurl, &top );
2307                break;
2308            }
2309            case 960:
2310            {
2311                tr_benc * args;
2312                tr_benc * top = tr_new0( tr_benc, 1 );
2313                tr_bencInitDict( top, 2 );
2314                tr_bencDictAddStr( top, "method", "torrent-set-location" );
2315                args = tr_bencDictAddDict( top, ARGUMENTS, 3 );
2316                tr_bencDictAddStr( args, "location", optarg );
2317                tr_bencDictAddBool( args, "move", true );
2318                addIdArg( args, id );
2319                status |= flush( rpcurl, &top );
2320                break;
2321            }
2322            default:
2323            {
2324                fprintf( stderr, "got opt [%d]\n", c );
2325                showUsage( );
2326                break;
2327            }
2328        }
2329    }
2330
2331    if( tadd != 0 ) status |= flush( rpcurl, &tadd );
2332    if( tset != 0 ) { addIdArg( tr_bencDictFind( tset, ARGUMENTS ), id ); status |= flush( rpcurl, &tset ); }
2333    if( sset != 0 ) status |= flush( rpcurl, &sset );
2334    return status;
2335}
2336
2337/* [host:port] or [host] or [port] or [http(s?)://host:port/transmission/] */
2338static void
2339getHostAndPortAndRpcUrl( int * argc, char ** argv,
2340                         char ** host, int * port, char ** rpcurl )
2341{
2342    if( *argv[1] != '-' )
2343    {
2344        int          i;
2345        const char * s = argv[1];
2346        const char * delim = strchr( s, ':' );
2347        if( !strncmp(s, "http://", 7 ) )   /* user passed in http rpc url */
2348        {
2349            *rpcurl = tr_strdup_printf( "%s/rpc/", s + 7 );
2350        }
2351        else if( !strncmp(s, "https://", 8) ) /* user passed in https rpc url */
2352        {
2353            UseSSL = true;
2354            *rpcurl = tr_strdup_printf( "%s/rpc/", s + 8 );
2355        }
2356        else if( delim )   /* user passed in both host and port */
2357        {
2358            *host = tr_strndup( s, delim - s );
2359            *port = atoi( delim + 1 );
2360        }
2361        else
2362        {
2363            char *    end;
2364            const int i = strtol( s, &end, 10 );
2365            if( !*end ) /* user passed in a port */
2366                *port = i;
2367            else /* user passed in a host */
2368                *host = tr_strdup( s );
2369        }
2370
2371        *argc -= 1;
2372        for( i = 1; i < *argc; ++i )
2373            argv[i] = argv[i + 1];
2374    }
2375}
2376
2377int
2378main( int argc, char ** argv )
2379{
2380    int port = DEFAULT_PORT;
2381    char * host = NULL;
2382    char * rpcurl = NULL;
2383    int exit_status = EXIT_SUCCESS;
2384
2385    if( argc < 2 ) {
2386        showUsage( );
2387        return EXIT_FAILURE;
2388    }
2389
2390    tr_formatter_mem_init( MEM_K, MEM_K_STR, MEM_M_STR, MEM_G_STR, MEM_T_STR );
2391    tr_formatter_size_init( DISK_K,DISK_K_STR, DISK_M_STR, DISK_G_STR, DISK_T_STR );
2392    tr_formatter_speed_init( SPEED_K, SPEED_K_STR, SPEED_M_STR, SPEED_G_STR, SPEED_T_STR );
2393
2394    getHostAndPortAndRpcUrl( &argc, argv, &host, &port, &rpcurl );
2395    if( host == NULL )
2396        host = tr_strdup( DEFAULT_HOST );
2397    if( rpcurl == NULL )
2398        rpcurl = tr_strdup_printf( "%s:%d%s", host, port, DEFAULT_URL );
2399
2400    exit_status = processArgs( rpcurl, argc, (const char**)argv );
2401
2402    tr_free( host );
2403    tr_free( rpcurl );
2404    return exit_status;
2405}
Note: See TracBrowser for help on using the repository browser.