source: trunk/daemon/remote.c @ 7970

Last change on this file since 7970 was 7970, checked in by charles, 13 years ago

(trunk daemon) #1833: sum-total line of speed up & down columns at the end of transmission-remote -l

  • Property svn:keywords set to Date Rev Author Id
File size: 45.0 KB
Line 
1/*
2 * This file Copyright (C) 2008-2009 Charles Kerr <charles@transmissionbt.com>
3 *
4 * This file is licensed by the GPL version 2.  Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: remote.c 7970 2009-02-27 15:59:09Z charles $
11 */
12
13#include <errno.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h> /* strcmp */
17
18#ifdef WIN32
19 #include <direct.h> /* getcwd */
20#else
21 #include <unistd.h> /* getcwd */
22#endif
23
24#include <libevent/event.h>
25
26#define CURL_DISABLE_TYPECHECK /* otherwise -Wunreachable-code goes insane */
27#include <curl/curl.h>
28
29#include <libtransmission/transmission.h>
30#include <libtransmission/bencode.h>
31#include <libtransmission/rpcimpl.h>
32#include <libtransmission/json.h>
33#include <libtransmission/tr-getopt.h>
34#include <libtransmission/utils.h>
35#include <libtransmission/version.h>
36
37#define MY_NAME "transmission-remote"
38#define DEFAULT_HOST "localhost"
39#define DEFAULT_PORT atoi(TR_DEFAULT_RPC_PORT_STR)
40
41enum { TAG_SESSION, TAG_LIST, TAG_DETAILS, TAG_FILES, TAG_PEERS };
42
43static const char*
44getUsage( void )
45{
46    return
47        "Transmission " LONG_VERSION_STRING
48        "  http://www.transmissionbt.com/\n"
49        "A fast and easy BitTorrent client\n"
50        "\n"
51        "Usage: " MY_NAME
52        " [host] [options]\n"
53        "       "
54        MY_NAME " [port] [options]\n"
55                "       "
56        MY_NAME " [host:port] [options]\n"
57                "\n"
58                "See the man page for detailed explanations and many examples.";
59}
60
61static tr_option opts[] =
62{
63    { 'a', "add",                  "Add torrent files by filename or URL", "a",  0, NULL },
64    { 'b', "debug",                "Print debugging information", "b",  0, NULL },
65    { 'd', "downlimit",            "Set the maximum global download speed in KB/s", "d",  1, "<speed>" },
66    { 'D', "no-downlimit",         "Don't limit the global download speed", "D",  0, NULL },
67    { 910, "encryption-required",  "Encrypt all peer connections", "er", 0, NULL },
68    { 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", 0, NULL },
69    { 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", 0, NULL },
70    { 'f', "files",                "List the current torrent(s)' files", "f",  0, NULL },
71    { 'g', "get",                  "Mark files for download", "g",  1, "<files>" },
72    { 'G', "no-get",               "Mark files for not downloading", "G",  1, "<files>" },
73    { 'i', "info",                 "Show the current torrent(s)' details", "i",  0, NULL },
74    { 920, "session-info",         "Show the session's details", "si", 0, NULL },
75    { 'l', "list",                 "List all torrents", "l",  0, NULL },
76    { 'm', "portmap",              "Enable portmapping via NAT-PMP or UPnP", "m",  0, NULL },
77    { 'M', "no-portmap",           "Disable portmapping", "M",  0, NULL },
78    { 'n', "auth",                 "Set authentication info", "n",  1, "<username:password>" },
79    { 'N', "netrc",                "Set authentication info from a .netrc file", "N",  1, "<filename>" },
80    { 'p', "port",                 "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "p", 1, "<port>" },
81    { 900, "priority-high",        "Set the files' priorities as high", "ph", 1, "<files>" },
82    { 901, "priority-normal",      "Set the files' priorities as normal", "pn", 1, "<files>" },
83    { 902, "priority-low",         "Set the files' priorities as low", "pl", 1, "<files>" },
84    { 'r', "remove",               "Remove the current torrent(s)", "r",  0, NULL },
85    { 930, "peers",                "Set the current torrent(s)' maximum number of peers each", "pr", 1, "<max>" },
86    { 931, "global-peers",         "Set the global maximum number of peers", "gpr", 1, "<max>" },
87    { 'R', "remove-and-delete",    "Remove the current torrent(s) and delete local data", NULL, 0, NULL },
88    { 950, "seedratio",            "Let the current torrent(s) seed until a specific ratio", "sr", 1, "ratio" },
89    { 951, "seedratio-default",    "Let the current torrent(s) use the global seedratio settings", "srd", 0, NULL },
90    { 952, "no-seedratio",         "Let the current torrent(s) seed regardless of ratio", "SR", 0, NULL },
91    { 953, "global-seedratio",     "All torrents, unless overridden by a per-torrent setting, should seed until a specific ratio", "gsr", 1, "ratio" },
92    { 954, "no-global-seedratio",  "All torrents, unless overridden by a per-torrent setting, should seed regardless of ratio", "GSR", 0, NULL },
93    { 's', "start",                "Start the current torrent(s)", "s",  0, NULL },
94    { 'S', "stop",                 "Stop the current torrent(s)", "S",  0, NULL },
95    { 't', "torrent",              "Set the current torrent(s)", "t",  1, "<torrent>" },
96    { 'u', "uplimit",              "Set the maximum global upload speed in KB/s", "u",  1, "<speed>" },
97    { 'U', "no-uplimit",           "Don't limit the global upload speed", "U",  0, NULL },
98    { 'v', "verify",               "Verify the current torrent(s)", "v",  0, NULL },
99    { 'V', "version",              "Show version number and exit", "V", 0, NULL },
100    { 'w', "download-dir",         "Set the default download folder", "w",  1, "<path>" },
101    { 'x', "pex",                  "Enable peer exchange (PEX)", "x",  0, NULL },
102    { 'X', "no-pex",               "Disable peer exchange (PEX)", "X",  0, NULL },
103    { 940, "peer-info",            "List the current torrent(s)' peers", "pi",  0, NULL },
104    {   0, NULL,                   NULL, NULL, 0, NULL }
105};
106
107static void
108showUsage( void )
109{
110    tr_getopt_usage( MY_NAME, getUsage( ), opts );
111    exit( 0 );
112}
113
114static int
115numarg( const char * arg )
116{
117    char *     end = NULL;
118    const long num = strtol( arg, &end, 10 );
119
120    if( *end )
121    {
122        fprintf( stderr, "Not a number: \"%s\"\n", arg );
123        showUsage( );
124    }
125    return num;
126}
127
128static char * reqs[256]; /* arbitrary max */
129static int    reqCount = 0;
130static int    debug = 0;
131static char * auth = NULL;
132static char * netrc = NULL;
133
134static char*
135tr_getcwd( void )
136{
137    char buf[2048];
138    *buf = '\0';
139#ifdef WIN32
140    _getcwd( buf, sizeof( buf ) );
141#else
142    getcwd( buf, sizeof( buf ) );
143#endif
144    return tr_strdup( buf );
145}
146
147static char*
148absolutify( const char * path )
149{
150    char * buf;
151
152    if( *path == '/' )
153        buf = tr_strdup( path );
154    else {
155        char * cwd = tr_getcwd( );
156        buf = tr_buildPath( cwd, path, NULL );
157        tr_free( cwd );
158    }
159
160    return buf;
161}
162
163static char*
164getEncodedMetainfo( const char * filename )
165{
166    size_t    len = 0;
167    char *    b64 = NULL;
168    uint8_t * buf = tr_loadFile( filename, &len );
169
170    if( buf )
171    {
172        b64 = tr_base64_encode( buf, len, NULL );
173        tr_free( buf );
174    }
175    return b64;
176}
177
178static void
179addIdArg( tr_benc *    args,
180          const char * id )
181{
182    if( !*id )
183    {
184        fprintf(
185            stderr,
186            "No torrent specified!  Please use the -t option first.\n" );
187        id = "-1"; /* no torrent will have this ID, so should be a no-op */
188    }
189    if( strcmp( id, "all" ) )
190    {
191        tr_rpc_parse_list_str( tr_bencDictAdd( args,
192                                               "ids" ), id, strlen( id ) );
193    }
194}
195
196static void
197addFiles( tr_benc *    args,
198          const char * key,
199          const char * arg )
200{
201    tr_benc * files = tr_bencDictAddList( args, key, 100 );
202
203    if( !*arg )
204    {
205        fprintf( stderr, "No files specified!\n" );
206        arg = "-1"; /* no file will have this index, so should be a no-op */
207    }
208    if( strcmp( arg, "all" ) )
209    {
210        int i;
211        int valueCount;
212        int * values = tr_parseNumberRange( arg, -1, &valueCount );
213        for( i=0; i<valueCount; ++i )
214            tr_bencListAddInt( files, values[i] );
215        tr_free( values );
216    }
217}
218
219#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) )
220
221static const char * files_keys[] = {
222    "files",
223    "name",
224    "priorities",
225    "wanted"
226};
227
228static const char * details_keys[] = {
229    "activityDate",
230    "addedDate",
231    "announceResponse",
232    "announceURL",
233    "comment",
234    "corruptEver",
235    "creator",
236    "dateCreated",
237    "doneDate",
238    "downloadDir",
239    "downloadedEver",
240    "errorString",
241    "eta",
242    "hashString",
243    "haveUnchecked",
244    "haveValid",
245    "id",
246    "isPrivate",
247    "lastAnnounceTime",
248    "lastScrapeTime",
249    "leechers",
250    "leftUntilDone",
251    "name",
252    "nextAnnounceTime",
253    "nextScrapeTime",
254    "peersConnected",
255    "peersGettingFromUs",
256    "peersSendingToUs",
257    "pieceCount",
258    "pieceSize",
259    "rateDownload",
260    "rateUpload",
261    "recheckProgress",
262    "scrapeResponse",
263    "seeders",
264    "sizeWhenDone",
265    "startDate",
266    "status",
267    "timesCompleted",
268    "totalSize",
269    "uploadedEver",
270    "webseeds",
271    "webseedsSendingToUs"
272};
273
274static const char * list_keys[] = {
275    "error",
276    "errorString",
277    "eta",
278    "id",
279    "leftUntilDone",
280    "name",
281    "peersGettingFromUs",
282    "peersSendingToUs",
283    "rateDownload",
284    "rateUpload",
285    "sizeWhenDone",
286    "status",
287    "uploadRatio"
288};
289
290static void
291readargs( int           argc,
292          const char ** argv )
293{
294    int          c;
295    int          addingTorrents = 0;
296    const char * optarg;
297    char         id[4096];
298
299    *id = '\0';
300
301    while( ( c = tr_getopt( getUsage( ), argc, argv, opts, &optarg ) ) )
302    {
303        int     i, n;
304        int     addArg = TRUE;
305        tr_benc top, *args, *fields;
306        tr_bencInitDict( &top, 3 );
307        args = tr_bencDictAddDict( &top, "arguments", 0 );
308
309        switch( c )
310        {
311            case TR_OPT_UNK:
312                if( addingTorrents )
313                {
314                    char * tmp = getEncodedMetainfo( optarg );
315                    if( tmp )
316                    {
317                        tr_bencDictAddStr( &top, "method", "torrent-add" );
318                        tr_bencDictAddStr( args, "metainfo", tmp );
319                        tr_free( tmp );
320                    }
321                    else
322                    {
323                        tr_bencDictAddStr( &top, "method", "torrent-add" );
324                        tr_bencDictAddStr( args, "filename", optarg );
325                    }
326                }
327                else
328                {
329                    fprintf( stderr, "Unknown option: %s\n", optarg );
330                    addArg = FALSE;
331                }
332                break;
333
334            case 'a':
335                addingTorrents = 1;
336                addArg = FALSE;
337                break;
338
339            case 'b':
340                debug = 1;
341                addArg = FALSE;
342                break;
343
344            case 'd':
345                tr_bencDictAddStr( &top, "method", "session-set" );
346                tr_bencDictAddInt( args, "speed-limit-down", numarg( optarg ) );
347                tr_bencDictAddInt( args, "speed-limit-down-enabled", 1 );
348                break;
349
350            case 'D':
351                tr_bencDictAddStr( &top, "method", "session-set" );
352                tr_bencDictAddInt( args, "speed-limit-down-enabled", 0 );
353                break;
354
355            case 'f':
356                tr_bencDictAddStr( &top, "method", "torrent-get" );
357                tr_bencDictAddInt( &top, "tag", TAG_FILES );
358                addIdArg( args, id );
359                n = TR_N_ELEMENTS( files_keys );
360                fields = tr_bencDictAddList( args, "fields", n );
361                for( i = 0; i < n; ++i )
362                    tr_bencListAddStr( fields, files_keys[i] );
363                break;
364
365            case 'g':
366                tr_bencDictAddStr( &top, "method", "torrent-set" );
367                addIdArg( args, id );
368                addFiles( args, "files-wanted", optarg );
369                break;
370
371            case 'G':
372                tr_bencDictAddStr( &top, "method", "torrent-set" );
373                addIdArg( args, id );
374                addFiles( args, "files-unwanted", optarg );
375                break;
376
377            case 'i':
378                tr_bencDictAddStr( &top, "method", "torrent-get" );
379                tr_bencDictAddInt( &top, "tag", TAG_DETAILS );
380                addIdArg( args, id );
381                n = TR_N_ELEMENTS( details_keys );
382                fields = tr_bencDictAddList( args, "fields", n );
383                for( i = 0; i < n; ++i )
384                    tr_bencListAddStr( fields, details_keys[i] );
385                break;
386
387            case 'l':
388                tr_bencDictAddStr( &top, "method", "torrent-get" );
389                tr_bencDictAddInt( &top, "tag", TAG_LIST );
390                n = TR_N_ELEMENTS( list_keys );
391                fields = tr_bencDictAddList( args, "fields", n );
392                for( i = 0; i < n; ++i )
393                    tr_bencListAddStr( fields, list_keys[i] );
394                break;
395
396            case 'm':
397                tr_bencDictAddStr( &top, "method", "session-set" );
398                tr_bencDictAddInt( args, "port-forwarding-enabled", 1 );
399                break;
400
401            case 'M':
402                tr_bencDictAddStr( &top, "method", "session-set" );
403                tr_bencDictAddInt( args, "port-forwarding-enabled", 0 );
404                break;
405
406            case 'n':
407                auth = tr_strdup( optarg );
408                addArg = FALSE;
409                break;
410
411            case 'N':
412                netrc = tr_strdup( optarg );
413                addArg = FALSE;
414                break;
415
416            case 'p':
417                tr_bencDictAddStr( &top, "method", "session-set" );
418                tr_bencDictAddInt( args, "port", numarg( optarg ) );
419                break;
420
421            case 'r':
422                tr_bencDictAddStr( &top, "method", "torrent-remove" );
423                addIdArg( args, id );
424                break;
425
426            case 'R':
427                tr_bencDictAddStr( &top, "method", "torrent-remove" );
428                addIdArg( args, id );
429                tr_bencDictAddInt( args, "delete-local-data", 1 );
430                break;
431
432            case 's':
433                tr_bencDictAddStr( &top, "method", "torrent-start" );
434                addIdArg( args, id );
435                break;
436
437            case 'S':
438                tr_bencDictAddStr( &top, "method", "torrent-stop" );
439                addIdArg( args, id );
440                break;
441
442            case 't':
443                tr_strlcpy( id, optarg, sizeof( id ) );
444                addArg = FALSE;
445                break;
446
447            case 'u':
448                tr_bencDictAddStr( &top, "method", "session-set" );
449                tr_bencDictAddInt( args, "speed-limit-up", numarg( optarg ) );
450                tr_bencDictAddInt( args, "speed-limit-up-enabled", 1 );
451                break;
452
453            case 'U':
454                tr_bencDictAddStr( &top, "method", "session-set" );
455                tr_bencDictAddInt( args, "speed-limit-up-enabled", 0 );
456                break;
457
458            case 'v':
459                tr_bencDictAddStr( &top, "method", "torrent-verify" );
460                addIdArg( args, id );
461                break;
462
463            case 'V':
464                fprintf( stderr, "Transmission %s\n", LONG_VERSION_STRING );
465                exit( 0 );
466                break;
467
468            case 'w': {
469                char * path = absolutify( optarg );
470                tr_bencDictAddStr( &top, "method", "session-set" );
471                tr_bencDictAddStr( args, "download-dir", path );
472                tr_free( path );
473                break;
474            }
475
476            case 'x':
477                tr_bencDictAddStr( &top, "method", "session-set" );
478                tr_bencDictAddInt( args, "pex-allowed", 1 );
479                break;
480
481            case 'X':
482                tr_bencDictAddStr( &top, "method", "session-set" );
483                tr_bencDictAddInt( args, "pex-allowed", 0 );
484                break;
485
486            case 900:
487                tr_bencDictAddStr( &top, "method", "torrent-set" );
488                addIdArg( args, id );
489                addFiles( args, "priority-high", optarg );
490                break;
491
492            case 901:
493                tr_bencDictAddStr( &top, "method", "torrent-set" );
494                addIdArg( args, id );
495                addFiles( args, "priority-normal", optarg );
496                break;
497
498            case 902:
499                tr_bencDictAddStr( &top, "method", "torrent-set" );
500                addIdArg( args, id );
501                addFiles( args, "priority-low", optarg );
502                break;
503
504            case 910:
505                tr_bencDictAddStr( &top, "method", "session-set" );
506                tr_bencDictAddStr( args, "encryption", "required" );
507                break;
508
509            case 911:
510                tr_bencDictAddStr( &top, "method", "session-set" );
511                tr_bencDictAddStr( args, "encryption", "preferred" );
512                break;
513
514            case 912:
515                tr_bencDictAddStr( &top, "method", "session-set" );
516                tr_bencDictAddStr( args, "encryption", "tolerated" );
517                break;
518
519            case 920:
520                tr_bencDictAddStr( &top, "method", "session-get" );
521                tr_bencDictAddInt( &top, "tag", TAG_SESSION );
522                break;
523
524            case 930:
525                tr_bencDictAddStr( &top, "method", "torrent-set" );
526                addIdArg( args, id );
527                tr_bencDictAddInt( args, "peer-limit", atoi(optarg) );
528                break;
529
530            case 931:
531                tr_bencDictAddStr( &top, "method", "session-set" );
532                tr_bencDictAddInt( args, "peer-limit", atoi(optarg) );
533                break;
534
535            case 940:
536                tr_bencDictAddStr( &top, "method", "torrent-get" );
537                tr_bencDictAddInt( &top, "tag", TAG_PEERS );
538                addIdArg( args, id );
539                fields = tr_bencDictAddList( args, "fields", 1 );
540                tr_bencListAddStr( fields, "peers" );
541                break;
542
543            case 950:
544                tr_bencDictAddStr( &top, "method", "torrent-set" );
545                tr_bencDictAddDouble( args, "ratio-limit", atof(optarg) );
546                tr_bencDictAddInt( args, "ratio-limit-mode", TR_RATIOLIMIT_SINGLE );
547                addIdArg( args, id );
548                break;
549
550            case 951:
551                tr_bencDictAddStr( &top, "method", "torrent-set" );
552                tr_bencDictAddInt( args, "ratio-limit-mode", TR_RATIOLIMIT_GLOBAL );
553                addIdArg( args, id );
554                break;
555
556            case 952:
557                tr_bencDictAddStr( &top, "method", "torrent-set" );
558                tr_bencDictAddInt( args, "ratio-limit-mode", TR_RATIOLIMIT_UNLIMITED );
559                addIdArg( args, id );
560                break;
561
562            case 953:
563                tr_bencDictAddStr( &top, "method", "session-set" );
564                tr_bencDictAddDouble( args, "ratio-limit", atof(optarg) );
565                tr_bencDictAddInt( args, "ratio-limit-enabled", 1 );
566                break;
567
568            case 954:
569                tr_bencDictAddStr( &top, "method", "session-set" );
570                tr_bencDictAddInt( args, "ratio-limit-enabled", 0 );
571                break;
572
573            case TR_OPT_ERR:
574                fprintf( stderr, "invalid option\n" );
575                showUsage( );
576                break;
577
578            default:
579                fprintf( stderr, "got opt [%d]\n", (int)c );
580                showUsage( );
581                break;
582        }
583
584        if( addArg )
585        {
586            struct evbuffer * buf = tr_getBuffer( );
587            reqs[reqCount++] = tr_strdup( tr_bencSaveAsJSON( &top, buf ) );
588            tr_releaseBuffer( buf );
589        }
590
591        tr_bencFree( &top );
592    }
593}
594
595/* [host:port] or [host] or [port] */
596static void
597getHostAndPort( int *   argc,
598                char ** argv,
599                char ** host,
600                int *   port )
601{
602    if( *argv[1] != '-' )
603    {
604        int          i;
605        const char * s = argv[1];
606        const char * delim = strchr( s, ':' );
607        if( delim )   /* user passed in both host and port */
608        {
609            *host = tr_strndup( s, delim - s );
610            *port = atoi( delim + 1 );
611        }
612        else
613        {
614            char *    end;
615            const int i = strtol( s, &end, 10 );
616            if( !*end ) /* user passed in a port */
617                *port = i;
618            else /* user passed in a host */
619                *host = tr_strdup( s );
620        }
621
622        *argc -= 1;
623        for( i = 1; i < *argc; ++i )
624            argv[i] = argv[i + 1];
625    }
626}
627
628static size_t
629writeFunc( void * ptr,
630           size_t size,
631           size_t nmemb,
632           void * buf )
633{
634    const size_t byteCount = size * nmemb;
635
636    evbuffer_add( buf, ptr, byteCount );
637    return byteCount;
638}
639
640static void
641etaToString( char *  buf,
642             size_t  buflen,
643             int64_t eta )
644{
645    if( eta < 0 ) tr_snprintf( buf, buflen, "Unknown" );
646    else if( eta < 60 ) tr_snprintf( buf, buflen, "%" PRId64 "sec", eta );
647    else if( eta <
648            ( 60 * 60 ) ) tr_snprintf( buf, buflen, "%" PRId64 " min",
649                                       eta / 60 );
650    else if( eta <
651            ( 60 * 60 * 24 ) ) tr_snprintf( buf, buflen, "%" PRId64 " hrs",
652                                           eta / ( 60 * 60 ) );
653    else tr_snprintf( buf, buflen, "%" PRId64 " days", eta / ( 60 * 60 * 24 ) );
654}
655
656#define KILOBYTE_FACTOR 1024.0
657#define MEGABYTE_FACTOR ( 1024.0 * 1024.0 )
658#define GIGABYTE_FACTOR ( 1024.0 * 1024.0 * 1024.0 )
659
660static char*
661strlratio2( char * buf, double ratio, size_t buflen )
662{
663    if( (int)ratio == TR_RATIO_NA )
664        tr_strlcpy( buf, "None", buflen );
665    else if( (int)ratio == TR_RATIO_INF )
666        tr_strlcpy( buf, "Inf", buflen );
667    else if( ratio < 10.0 )
668        tr_snprintf( buf, buflen, "%'.2f", ratio );
669    else if( ratio < 100.0 )
670        tr_snprintf( buf, buflen, "%'.1f", ratio );
671    else
672        tr_snprintf( buf, buflen, "%'.0f", ratio );
673    return buf;
674}
675
676static char*
677strlratio( char * buf,
678           double numerator,
679           double denominator,
680           size_t buflen )
681{
682    double ratio;
683
684    if( denominator )
685        ratio = numerator / denominator;
686    else if( numerator )
687        ratio = TR_RATIO_INF;
688    else
689        ratio = TR_RATIO_NA;
690
691    return strlratio2( buf, ratio, buflen );
692}
693
694static char*
695strlsize( char *  buf, int64_t size, size_t  buflen )
696{
697    if( !size )
698        tr_strlcpy( buf, "None", buflen );
699    else if( size < (int64_t)KILOBYTE_FACTOR )
700        tr_snprintf( buf, buflen, "%'" PRId64 " bytes", (int64_t)size );
701    else
702    {
703        double displayed_size;
704        if( size < (int64_t)MEGABYTE_FACTOR )
705        {
706            displayed_size = (double) size / KILOBYTE_FACTOR;
707            tr_snprintf( buf, buflen, "%'.1f KB", displayed_size );
708        }
709        else if( size < (int64_t)GIGABYTE_FACTOR )
710        {
711            displayed_size = (double) size / MEGABYTE_FACTOR;
712            tr_snprintf( buf, buflen, "%'.1f MB", displayed_size );
713        }
714        else
715        {
716            displayed_size = (double) size / GIGABYTE_FACTOR;
717            tr_snprintf( buf, buflen, "%'.1f GB", displayed_size );
718        }
719    }
720    return buf;
721}
722
723static char*
724getStatusString( tr_benc * t, char * buf, size_t buflen )
725{
726    int64_t status;
727
728    if( !tr_bencDictFindInt( t, "status", &status ) )
729    {
730        *buf = '\0';
731    }
732    else switch( status )
733    {
734        case TR_STATUS_STOPPED:
735            tr_strlcpy( buf, "Stopped", buflen );
736            break;
737
738        case TR_STATUS_CHECK_WAIT:
739        case TR_STATUS_CHECK: {
740            const char * str = status == TR_STATUS_CHECK_WAIT
741                             ? "Will Verify"
742                             : "Verifying";
743            double percent;
744            if( tr_bencDictFindDouble( t, "recheckProgress", &percent ) )
745                tr_snprintf( buf, buflen, "%s (%.0f%%)", str, percent*100.0 );
746            else
747                tr_strlcpy( buf, str, buflen );
748
749            break;
750        }
751
752        case TR_STATUS_DOWNLOAD:
753        case TR_STATUS_SEED: {
754            int64_t fromUs = 0; 
755            int64_t toUs = 0; 
756            tr_bencDictFindInt( t, "peersGettingFromUs", &fromUs ); 
757            tr_bencDictFindInt( t, "peersSendingToUs", &toUs ); 
758            if( fromUs && toUs ) 
759                tr_strlcpy( buf, "Up & Down", buflen ); 
760            else if( toUs ) 
761                tr_strlcpy( buf, "Downloading", buflen ); 
762            else if( fromUs ) { 
763                int64_t leftUntilDone = 0; 
764                tr_bencDictFindInt( t, "leftUntilDone", &leftUntilDone ); 
765                if( leftUntilDone > 0 )
766                    tr_strlcpy( buf, "Uploading", buflen ); 
767                else
768                    tr_strlcpy( buf, "Seeding", buflen ); 
769            } else {
770                tr_strlcpy( buf, "Idle", buflen ); 
771            }
772            break;
773        }
774    }
775
776    return buf;
777}
778
779static const char*
780getTrackerDateStr( const time_t t, tr_bool isStopped )
781{
782    const char * str;
783    switch( t ) {
784        case 0: str = isStopped ? "None (Stopped)\n" : "None\n"; break;
785        case 1: str = "In Progress\n"; break;
786        default: str = ctime( &t ); break;
787    }
788    return str;
789}
790
791static void
792printSession( tr_benc * top )
793{
794    tr_benc *args;
795    if( ( tr_bencDictFindDict( top, "arguments", &args ) ) )
796    {
797        const char * str;
798        int64_t      i;
799
800        printf( "VERSION\n" );
801        if( tr_bencDictFindStr( args,  "version", &str ) )
802            printf( "  Daemon version: %s\n", str );
803        if( tr_bencDictFindInt( args, "rpc-version", &i ) )
804            printf( "  RPC version: %" PRId64 "\n", i );
805        if( tr_bencDictFindInt( args, "rpc-version-minimum", &i ) )
806            printf( "  RPC minimum version: %" PRId64 "\n", i );
807        printf( "\n" );
808
809        printf( "TRANSFER\n" );
810        if( tr_bencDictFindStr( args,  "download-dir", &str ) )
811            printf( "  Download directory: %s\n", str );
812        if( tr_bencDictFindInt( args, "port", &i ) )
813            printf( "  Listenport: %" PRId64 "\n", i );
814        if( tr_bencDictFindInt( args, "port-forwarding-enabled", &i ) )
815            printf( "  Portforwarding enabled: %s\n", ( i ? "Yes" : "No" ) );
816        if( tr_bencDictFindInt( args, "pex-allowed", &i ) )
817            printf( "  Peer exchange allowed: %s\n", ( i ? "Yes" : "No" ) );
818        if( tr_bencDictFindStr( args,  "encryption", &str ) )
819            printf( "  Encryption: %s\n", str );
820        printf( "\n" );
821
822        printf( "LIMITS\n" );
823        if( tr_bencDictFindInt( args, "peer-limit", &i ) )
824            printf( "  Peer limit: %" PRId64 "\n", i );
825        if( tr_bencDictFindInt( args, "speed-limit-down-enabled", &i ) )
826            printf( "  Downloadlimit enabled: %s\n", ( i ? "Yes" : "No" ) );
827        if( tr_bencDictFindInt( args, "speed-limit-down", &i ) )
828            printf( "  Downloadlimit: %6" PRId64 " KB/sec\n", i );
829        if( tr_bencDictFindInt( args, "speed-limit-up-enabled", &i ) )
830            printf( "  Uploadlimit enabled:   %s\n", ( i ? "Yes" : "No" ) );
831        if( tr_bencDictFindInt( args, "speed-limit-up", &i ) )
832            printf( "  Uploadlimit:   %6" PRId64 " KB/sec\n", i );
833    }
834}
835
836static void
837printDetails( tr_benc * top )
838{
839    tr_benc *args, *torrents;
840
841    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
842      && ( tr_bencDictFindList( args, "torrents", &torrents ) ) )
843    {
844        int ti, tCount;
845        for( ti = 0, tCount = tr_bencListSize( torrents ); ti < tCount;
846             ++ti )
847        {
848            tr_benc *    t = tr_bencListChild( torrents, ti );
849            tr_benc *    l;
850            const char * str;
851            char         buf[512];
852            char         buf2[512];
853            int64_t      i, j, k;
854            tr_bool      isStopped;
855
856            isStopped = tr_bencDictFindInt( t, "status", &i ) && (i==TR_STATUS_STOPPED);
857
858            printf( "NAME\n" );
859            if( tr_bencDictFindInt( t, "id", &i ) )
860                printf( "  Id: %" PRId64 "\n", i );
861            if( tr_bencDictFindStr( t, "name", &str ) )
862                printf( "  Name: %s\n", str );
863            if( tr_bencDictFindStr( t, "hashString", &str ) )
864                printf( "  Hash: %s\n", str );
865            printf( "\n" );
866
867            printf( "TRANSFER\n" );
868            getStatusString( t, buf, sizeof( buf ) );
869            printf( "  State: %s\n", buf );
870
871            if( tr_bencDictFindStr( t, "downloadDir", &str ) )
872                printf( "  Location: %s\n", str );
873
874            if( tr_bencDictFindInt( t, "sizeWhenDone", &i )
875              && tr_bencDictFindInt( t, "leftUntilDone", &j ) )
876            {
877                strlratio( buf, 100.0 * ( i - j ), i, sizeof( buf ) );
878                printf( "  Percent Done: %s%%\n", buf );
879            }
880
881            if( tr_bencDictFindInt( t, "eta", &i ) )
882            {
883                etaToString( buf, sizeof( buf ), i );
884                printf( "  ETA: %s\n", buf );
885            }
886            if( tr_bencDictFindInt( t, "rateDownload", &i ) )
887                printf( "  Download Speed: %.1f KB/s\n", i / 1024.0 );
888            if( tr_bencDictFindInt( t, "rateUpload", &i ) )
889                printf( "  Upload Speed: %.1f KB/s\n", i / 1024.0 );
890            if( tr_bencDictFindInt( t, "haveUnchecked", &i )
891              && tr_bencDictFindInt( t, "haveValid", &j ) )
892            {
893                strlsize( buf, i + j, sizeof( buf ) );
894                strlsize( buf2, j, sizeof( buf2 ) );
895                printf( "  Have: %s (%s verified)\n", buf, buf2 );
896            }
897
898            if( tr_bencDictFindInt( t, "sizeWhenDone", &i )
899              && tr_bencDictFindInt( t, "totalSize", &j ) )
900            {
901                strlsize( buf, j, sizeof( buf ) );
902                strlsize( buf2, i, sizeof( buf2 ) );
903                printf( "  Total size: %s (%s wanted)\n", buf, buf2 );
904            }
905            if( tr_bencDictFindInt( t, "downloadedEver", &i )
906              && tr_bencDictFindInt( t, "uploadedEver", &j ) )
907            {
908                strlsize( buf, i, sizeof( buf ) );
909                printf( "  Downloaded: %s\n", buf );
910                strlsize( buf, j, sizeof( buf ) );
911                printf( "  Uploaded: %s\n", buf );
912                strlratio( buf, j, i, sizeof( buf ) );
913                printf( "  Ratio: %s\n", buf );
914            }
915            if( tr_bencDictFindInt( t, "corruptEver", &i ) )
916            {
917                strlsize( buf, i, sizeof( buf ) );
918                printf( "  Corrupt DL: %s\n", buf );
919            }
920            if( tr_bencDictFindStr( t, "errorString", &str ) && str && *str )
921                printf( "  Error: %s\n", str );
922
923            if( tr_bencDictFindInt( t, "peersConnected", &i )
924              && tr_bencDictFindInt( t, "peersGettingFromUs", &j )
925              && tr_bencDictFindInt( t, "peersSendingToUs", &k ) )
926            {
927                printf(
928                    "  Peers: "
929                    "connected to %" PRId64 ", "
930                                            "uploading to %" PRId64
931                    ", "
932                    "downloading from %"
933                    PRId64 "\n",
934                    i, j, k );
935            }
936
937            if( tr_bencDictFindList( t, "webseeds", &l )
938              && tr_bencDictFindInt( t, "webseedsSendingToUs", &i ) )
939            {
940                const int64_t n = tr_bencListSize( l );
941                if( n > 0 )
942                    printf(
943                        "  Web Seeds: downloading from %" PRId64 " of %"
944                        PRId64
945                        " web seeds\n", i, n );
946            }
947            printf( "\n" );
948
949            printf( "HISTORY\n" );
950            if( tr_bencDictFindInt( t, "addedDate", &i ) && i )
951            {
952                const time_t tt = i;
953                printf( "  Date added:      %s", ctime( &tt ) );
954            }
955            if( tr_bencDictFindInt( t, "doneDate", &i ) && i )
956            {
957                const time_t tt = i;
958                printf( "  Date finished:   %s", ctime( &tt ) );
959            }
960            if( tr_bencDictFindInt( t, "startDate", &i ) && i )
961            {
962                const time_t tt = i;
963                printf( "  Date started:    %s", ctime( &tt ) );
964            }
965            if( tr_bencDictFindInt( t, "activityDate", &i ) && i )
966            {
967                const time_t tt = i;
968                printf( "  Latest activity: %s", ctime( &tt ) );
969            }
970            printf( "\n" );
971
972            printf( "TRACKER\n" );
973            if( tr_bencDictFindInt( t, "lastAnnounceTime", &i ) )
974                printf( "  Latest announce: %s", getTrackerDateStr( (time_t)i, isStopped ) );
975            if( tr_bencDictFindStr( t, "announceURL", &str ) )
976                printf( "  Announce URL: %s\n", str );
977            if( tr_bencDictFindStr( t, "announceResponse", &str ) && str && *str )
978                printf( "  Announce response: %s\n", str );
979            if( tr_bencDictFindInt( t, "nextAnnounceTime", &i ) )
980                printf( "  Next announce:   %s", getTrackerDateStr( (time_t)i, isStopped ) );
981            if( tr_bencDictFindInt( t, "lastScrapeTime", &i ) )
982                printf( "  Latest scrape:   %s", getTrackerDateStr( (time_t)i, isStopped ) );
983            if( tr_bencDictFindStr( t, "scrapeResponse", &str ) )
984                printf( "  Scrape response: %s\n", str );
985            if( tr_bencDictFindInt( t, "nextScrapeTime", &i ) )
986                printf( "  Next scrape:     %s", getTrackerDateStr( (time_t)i, isStopped ) );
987            if( tr_bencDictFindInt( t, "seeders", &i ) && tr_bencDictFindInt( t, "leechers", &j ) )
988                printf( "  Tracker knows of %" PRId64 " seeders and %" PRId64 " leechers\n", i, j );
989            if( tr_bencDictFindInt( t, "timesCompleted", &i ) )
990                printf( "  Tracker has seen %" PRId64 " clients complete this torrent\n", i );
991            printf( "\n" );
992
993            printf( "ORIGINS\n" );
994            if( tr_bencDictFindInt( t, "dateCreated", &i ) && i )
995            {
996                const time_t tt = i;
997                printf( "  Date created: %s", ctime( &tt ) );
998            }
999            if( tr_bencDictFindInt( t, "isPrivate", &i ) )
1000                printf( "  Public torrent: %s\n", ( i ? "No" : "Yes" ) );
1001            if( tr_bencDictFindStr( t, "comment", &str ) && str && *str )
1002                printf( "  Comment: %s\n", str );
1003            if( tr_bencDictFindStr( t, "creator", &str ) && str && *str )
1004                printf( "  Creator: %s\n", str );
1005            if( tr_bencDictFindInt( t, "pieceCount", &i ) )
1006                printf( "  Piece Count: %" PRId64 "\n", i );
1007            if( tr_bencDictFindInt( t, "pieceSize", &i ) )
1008                printf( "  Piece Size: %" PRId64 "\n", i );
1009        }
1010    }
1011}
1012
1013static void
1014printFileList( tr_benc * top )
1015{
1016    tr_benc *args, *torrents;
1017
1018    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
1019      && ( tr_bencDictFindList( args, "torrents", &torrents ) ) )
1020    {
1021        int i, in;
1022        for( i = 0, in = tr_bencListSize( torrents ); i < in; ++i )
1023        {
1024            tr_benc *    d = tr_bencListChild( torrents, i );
1025            tr_benc *    files, *priorities, *wanteds;
1026            const char * name;
1027            if( tr_bencDictFindStr( d, "name", &name )
1028              && tr_bencDictFindList( d, "files", &files )
1029              && tr_bencDictFindList( d, "priorities", &priorities )
1030              && tr_bencDictFindList( d, "wanted", &wanteds ) )
1031            {
1032                int j = 0, jn = tr_bencListSize( files );
1033                printf( "%s (%d files):\n", name, jn );
1034                printf( "%3s  %4s %8s %3s %9s  %s\n", "#", "Done",
1035                        "Priority", "Get", "Size",
1036                        "Name" );
1037                for( j = 0, jn = tr_bencListSize( files ); j < jn; ++j )
1038                {
1039                    int64_t      have;
1040                    int64_t      length;
1041                    int64_t      priority;
1042                    int64_t      wanted;
1043                    const char * filename;
1044                    tr_benc *    file = tr_bencListChild( files, j );
1045                    if( tr_bencDictFindInt( file, "length", &length )
1046                      && tr_bencDictFindStr( file, "name", &filename )
1047                      && tr_bencDictFindInt( file, "bytesCompleted", &have )
1048                      && tr_bencGetInt( tr_bencListChild( priorities,
1049                                                          j ), &priority )
1050                      && tr_bencGetInt( tr_bencListChild( wanteds,
1051                                                          j ), &wanted ) )
1052                    {
1053                        char         sizestr[64];
1054                        double       percent = (double)have / length;
1055                        const char * pristr;
1056                        strlsize( sizestr, length, sizeof( sizestr ) );
1057                        switch( priority )
1058                        {
1059                            case TR_PRI_LOW:
1060                                pristr = "Low"; break;
1061
1062                            case TR_PRI_HIGH:
1063                                pristr = "High"; break;
1064
1065                            default:
1066                                pristr = "Normal"; break;
1067                        }
1068                        printf( "%3d: %3.0f%% %-8s %-3s %9s  %s\n",
1069                                j,
1070                                ( 100.0 * percent ),
1071                                pristr,
1072                                ( wanted ? "Yes" : "No" ),
1073                                sizestr,
1074                                filename );
1075                    }
1076                }
1077            }
1078        }
1079    }
1080}
1081
1082static void
1083printPeersImpl( tr_benc * peers )
1084{
1085    int i, n;
1086    printf( "%-20s  %-12s  %-5s %-6s  %-6s  %s\n",
1087            "Address", "Flags", "Done", "Down", "Up", "Client" );
1088    for( i = 0, n = tr_bencListSize( peers ); i < n; ++i )
1089    {
1090        double progress;
1091        const char * address, * client, * flagstr;
1092        int64_t rateToClient, rateToPeer;
1093        tr_benc * d = tr_bencListChild( peers, i );
1094
1095        if( tr_bencDictFindStr( d, "address", &address )
1096          && tr_bencDictFindStr( d, "clientName", &client )
1097          && tr_bencDictFindDouble( d, "progress", &progress )
1098          && tr_bencDictFindStr( d, "flagStr", &flagstr )
1099          && tr_bencDictFindInt( d, "rateToClient", &rateToClient )
1100          && tr_bencDictFindInt( d, "rateToPeer", &rateToPeer ) )
1101        {
1102            printf( "%-20s  %-12s  %-5.1f %6.1f  %6.1f  %s\n",
1103                    address, flagstr, (progress*100.0),
1104                    rateToClient / 1024.0,
1105                    rateToPeer / 1024.0,
1106                    client );
1107        }
1108    }
1109}
1110
1111static void
1112printPeers( tr_benc * top )
1113{
1114    tr_benc *args, *torrents;
1115
1116    if( tr_bencDictFindDict( top, "arguments", &args )
1117      && tr_bencDictFindList( args, "torrents", &torrents ) )
1118    {
1119        int i, n;
1120        for( i=0, n=tr_bencListSize( torrents ); i<n; ++i )
1121        {
1122            tr_benc * peers;
1123            tr_benc * torrent = tr_bencListChild( torrents, i );
1124            if( tr_bencDictFindList( torrent, "peers", &peers ) ) {
1125                printPeersImpl( peers );
1126                if( i+1<n )
1127                    printf( "\n" );
1128            }
1129        }
1130    }
1131}
1132
1133static void
1134printTorrentList( tr_benc * top )
1135{
1136    tr_benc *args, *list;
1137
1138    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
1139      && ( tr_bencDictFindList( args, "torrents", &list ) ) )
1140    {
1141        int i, n;
1142        int64_t total_up = 0, total_down = 0, total_size = 0;
1143        char haveStr[32];
1144
1145        printf( "%-4s   %-4s  %9s  %-8s  %6s  %6s  %-5s  %-11s  %s\n",
1146                "ID", "Done", "Have", "ETA", "Up", "Down", "Ratio", "Status",
1147                "Name" );
1148
1149        for( i = 0, n = tr_bencListSize( list ); i < n; ++i )
1150        {
1151            int64_t      id, eta, status, up, down;
1152            int64_t      sizeWhenDone, leftUntilDone;
1153            double       ratio;
1154            const char * name;
1155            tr_benc *   d = tr_bencListChild( list, i );
1156            if( tr_bencDictFindInt( d, "eta", &eta )
1157              && tr_bencDictFindInt( d, "id", &id )
1158              && tr_bencDictFindInt( d, "leftUntilDone", &leftUntilDone )
1159              && tr_bencDictFindStr( d, "name", &name )
1160              && tr_bencDictFindInt( d, "rateDownload", &down )
1161              && tr_bencDictFindInt( d, "rateUpload", &up )
1162              && tr_bencDictFindInt( d, "sizeWhenDone", &sizeWhenDone )
1163              && tr_bencDictFindInt( d, "status", &status )
1164              && tr_bencDictFindDouble( d, "uploadRatio", &ratio ) )
1165            {
1166                char etaStr[16];
1167                char statusStr[64];
1168                char ratioStr[32];
1169                char doneStr[8];
1170                int64_t error;
1171                char errorMark;
1172
1173                if( sizeWhenDone )
1174                    tr_snprintf( doneStr, sizeof( doneStr ), "%d%%", (int)( 100.0 * ( sizeWhenDone - leftUntilDone ) / sizeWhenDone ) );
1175                else
1176                    tr_strlcpy( doneStr, "n/a", sizeof( doneStr ) );
1177
1178                strlsize( haveStr, sizeWhenDone - leftUntilDone, sizeof( haveStr ) );
1179
1180                if( leftUntilDone )
1181                    etaToString( etaStr, sizeof( etaStr ), eta );
1182                else
1183                    tr_snprintf( etaStr, sizeof( etaStr ), "Done" );
1184                if( tr_bencDictFindInt( d, "error", &error ) && error )
1185                    errorMark = '*';
1186                else
1187                    errorMark = ' ';
1188                printf(
1189                    "%4d%c  %4s  %9s  %-8s  %6.1f  %6.1f  %5s  %-11s  %s\n",
1190                    (int)id, errorMark,
1191                    doneStr,
1192                    haveStr,
1193                    etaStr,
1194                    up / 1024.0,
1195                    down / 1024.0,
1196                    strlratio2( ratioStr, ratio, sizeof( ratioStr ) ),
1197                    getStatusString( d, statusStr, sizeof( statusStr ) ),
1198                    name );
1199
1200                total_up += up;
1201                total_down += down;
1202                total_size += sizeWhenDone - leftUntilDone;
1203            }
1204        }
1205
1206        printf( "Sum:         %9s             %6.1f  %6.1f\n",
1207                strlsize( haveStr, total_size, sizeof( haveStr ) ),
1208                total_up / 1024.0,
1209                total_down / 1024.0 );
1210    }
1211}
1212
1213static void
1214processResponse( const char * host,
1215                 int          port,
1216                 const void * response,
1217                 size_t       len )
1218{
1219    tr_benc top;
1220
1221    if( debug )
1222        fprintf( stderr, "got response:\n--------\n%*.*s\n--------\n",
1223                 (int)len, (int)len, (const char*) response );
1224
1225    if( tr_jsonParse( response, len, &top, NULL ) )
1226        tr_nerr( MY_NAME, "Unable to parse response \"%*.*s\"", (int)len,
1227                 (int)len, (char*)response );
1228    else
1229    {
1230        int64_t      tag = -1;
1231        const char * str;
1232        tr_bencDictFindInt( &top, "tag", &tag );
1233
1234        switch( tag )
1235        {
1236            case TAG_SESSION:
1237                printSession( &top ); break;
1238
1239            case TAG_FILES:
1240                printFileList( &top ); break;
1241
1242            case TAG_DETAILS:
1243                printDetails( &top ); break;
1244
1245            case TAG_LIST:
1246                printTorrentList( &top ); break;
1247
1248            case TAG_PEERS:
1249                printPeers( &top ); break;
1250
1251            default:
1252                if( tr_bencDictFindStr( &top, "result", &str ) )
1253                    printf( "%s:%d responded: \"%s\"\n", host, port, str );
1254        }
1255
1256        tr_bencFree( &top );
1257    }
1258}
1259
1260static void
1261processRequests( const char *  host,
1262                 int           port,
1263                 const char ** reqs,
1264                 int           reqCount )
1265{
1266    int               i;
1267    CURL *            curl;
1268    struct evbuffer * buf = evbuffer_new( );
1269    char *            url = tr_strdup_printf(
1270        "http://%s:%d/transmission/rpc", host, port );
1271
1272    curl = curl_easy_init( );
1273    curl_easy_setopt( curl, CURLOPT_VERBOSE, debug );
1274#ifdef HAVE_LIBZ
1275    curl_easy_setopt( curl, CURLOPT_ENCODING, "deflate" );
1276#endif
1277    curl_easy_setopt( curl, CURLOPT_USERAGENT, MY_NAME "/" LONG_VERSION_STRING );
1278    curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, writeFunc );
1279    curl_easy_setopt( curl, CURLOPT_WRITEDATA, buf );
1280    curl_easy_setopt( curl, CURLOPT_POST, 1 );
1281    curl_easy_setopt( curl, CURLOPT_URL, url );
1282    curl_easy_setopt( curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL );
1283    curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
1284    if( netrc )
1285        curl_easy_setopt( curl, CURLOPT_NETRC_FILE, netrc );
1286    if( auth )
1287        curl_easy_setopt( curl, CURLOPT_USERPWD, auth );
1288
1289    for( i = 0; i < reqCount; ++i )
1290    {
1291        CURLcode res;
1292        curl_easy_setopt( curl, CURLOPT_POSTFIELDS, reqs[i] );
1293        if( debug )
1294            fprintf( stderr, "posting:\n--------\n%s\n--------\n", reqs[i] );
1295        if( ( res = curl_easy_perform( curl ) ) )
1296            tr_nerr( MY_NAME, "(%s:%d) %s", host, port,
1297                    curl_easy_strerror( res ) );
1298        else
1299            processResponse( host, port, EVBUFFER_DATA(
1300                                buf ), EVBUFFER_LENGTH( buf ) );
1301
1302        evbuffer_drain( buf, EVBUFFER_LENGTH( buf ) );
1303    }
1304
1305    /* cleanup */
1306    tr_free( url );
1307    evbuffer_free( buf );
1308    curl_easy_cleanup( curl );
1309}
1310
1311int
1312main( int     argc,
1313      char ** argv )
1314{
1315    int    i;
1316    int    port = DEFAULT_PORT;
1317    char * host = NULL;
1318
1319    if( argc < 2 )
1320        showUsage( );
1321
1322    getHostAndPort( &argc, argv, &host, &port );
1323    if( host == NULL )
1324        host = tr_strdup( DEFAULT_HOST );
1325
1326    readargs( argc, (const char**)argv );
1327    if( reqCount )
1328        processRequests( host, port, (const char**)reqs, reqCount );
1329    else
1330        showUsage( );
1331
1332    for( i = 0; i < reqCount; ++i )
1333        tr_free( reqs[i] );
1334
1335    tr_free( host );
1336    return 0;
1337}
1338
Note: See TracBrowser for help on using the repository browser.