source: trunk/daemon/remote.c @ 8017

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

(trunk) #1881: promote tr_strratio() to libtransmission, so that its code doesn't have to be repeated in all the clients

  • Property svn:keywords set to Date Rev Author Id
File size: 45.4 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 8017 2009-03-04 16:16:02Z 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    "pieces",
271    "webseeds",
272    "webseedsSendingToUs"
273};
274
275static const char * list_keys[] = {
276    "error",
277    "errorString",
278    "eta",
279    "id",
280    "leftUntilDone",
281    "name",
282    "peersGettingFromUs",
283    "peersSendingToUs",
284    "rateDownload",
285    "rateUpload",
286    "sizeWhenDone",
287    "status",
288    "uploadRatio"
289};
290
291static void
292readargs( int           argc,
293          const char ** argv )
294{
295    int          c;
296    int          addingTorrents = 0;
297    const char * optarg;
298    char         id[4096];
299
300    *id = '\0';
301
302    while( ( c = tr_getopt( getUsage( ), argc, argv, opts, &optarg ) ) )
303    {
304        int     i, n;
305        int     addArg = TRUE;
306        tr_benc top, *args, *fields;
307        tr_bencInitDict( &top, 3 );
308        args = tr_bencDictAddDict( &top, "arguments", 0 );
309
310        switch( c )
311        {
312            case TR_OPT_UNK:
313                if( addingTorrents )
314                {
315                    char * tmp = getEncodedMetainfo( optarg );
316                    if( tmp )
317                    {
318                        tr_bencDictAddStr( &top, "method", "torrent-add" );
319                        tr_bencDictAddStr( args, "metainfo", tmp );
320                        tr_free( tmp );
321                    }
322                    else
323                    {
324                        tr_bencDictAddStr( &top, "method", "torrent-add" );
325                        tr_bencDictAddStr( args, "filename", optarg );
326                    }
327                }
328                else
329                {
330                    fprintf( stderr, "Unknown option: %s\n", optarg );
331                    addArg = FALSE;
332                }
333                break;
334
335            case 'a':
336                addingTorrents = 1;
337                addArg = FALSE;
338                break;
339
340            case 'b':
341                debug = 1;
342                addArg = FALSE;
343                break;
344
345            case 'd':
346                tr_bencDictAddStr( &top, "method", "session-set" );
347                tr_bencDictAddInt( args, "speed-limit-down", numarg( optarg ) );
348                tr_bencDictAddInt( args, "speed-limit-down-enabled", 1 );
349                break;
350
351            case 'D':
352                tr_bencDictAddStr( &top, "method", "session-set" );
353                tr_bencDictAddInt( args, "speed-limit-down-enabled", 0 );
354                break;
355
356            case 'f':
357                tr_bencDictAddStr( &top, "method", "torrent-get" );
358                tr_bencDictAddInt( &top, "tag", TAG_FILES );
359                addIdArg( args, id );
360                n = TR_N_ELEMENTS( files_keys );
361                fields = tr_bencDictAddList( args, "fields", n );
362                for( i = 0; i < n; ++i )
363                    tr_bencListAddStr( fields, files_keys[i] );
364                break;
365
366            case 'g':
367                tr_bencDictAddStr( &top, "method", "torrent-set" );
368                addIdArg( args, id );
369                addFiles( args, "files-wanted", optarg );
370                break;
371
372            case 'G':
373                tr_bencDictAddStr( &top, "method", "torrent-set" );
374                addIdArg( args, id );
375                addFiles( args, "files-unwanted", optarg );
376                break;
377
378            case 'i':
379                tr_bencDictAddStr( &top, "method", "torrent-get" );
380                tr_bencDictAddInt( &top, "tag", TAG_DETAILS );
381                addIdArg( args, id );
382                n = TR_N_ELEMENTS( details_keys );
383                fields = tr_bencDictAddList( args, "fields", n );
384                for( i = 0; i < n; ++i )
385                    tr_bencListAddStr( fields, details_keys[i] );
386                break;
387
388            case 'l':
389                tr_bencDictAddStr( &top, "method", "torrent-get" );
390                tr_bencDictAddInt( &top, "tag", TAG_LIST );
391                n = TR_N_ELEMENTS( list_keys );
392                fields = tr_bencDictAddList( args, "fields", n );
393                for( i = 0; i < n; ++i )
394                    tr_bencListAddStr( fields, list_keys[i] );
395                break;
396
397            case 'm':
398                tr_bencDictAddStr( &top, "method", "session-set" );
399                tr_bencDictAddInt( args, "port-forwarding-enabled", 1 );
400                break;
401
402            case 'M':
403                tr_bencDictAddStr( &top, "method", "session-set" );
404                tr_bencDictAddInt( args, "port-forwarding-enabled", 0 );
405                break;
406
407            case 'n':
408                auth = tr_strdup( optarg );
409                addArg = FALSE;
410                break;
411
412            case 'N':
413                netrc = tr_strdup( optarg );
414                addArg = FALSE;
415                break;
416
417            case 'p':
418                tr_bencDictAddStr( &top, "method", "session-set" );
419                tr_bencDictAddInt( args, "port", numarg( optarg ) );
420                break;
421
422            case 'r':
423                tr_bencDictAddStr( &top, "method", "torrent-remove" );
424                addIdArg( args, id );
425                break;
426
427            case 'R':
428                tr_bencDictAddStr( &top, "method", "torrent-remove" );
429                addIdArg( args, id );
430                tr_bencDictAddInt( args, "delete-local-data", 1 );
431                break;
432
433            case 's':
434                tr_bencDictAddStr( &top, "method", "torrent-start" );
435                addIdArg( args, id );
436                break;
437
438            case 'S':
439                tr_bencDictAddStr( &top, "method", "torrent-stop" );
440                addIdArg( args, id );
441                break;
442
443            case 't':
444                tr_strlcpy( id, optarg, sizeof( id ) );
445                addArg = FALSE;
446                break;
447
448            case 'u':
449                tr_bencDictAddStr( &top, "method", "session-set" );
450                tr_bencDictAddInt( args, "speed-limit-up", numarg( optarg ) );
451                tr_bencDictAddInt( args, "speed-limit-up-enabled", 1 );
452                break;
453
454            case 'U':
455                tr_bencDictAddStr( &top, "method", "session-set" );
456                tr_bencDictAddInt( args, "speed-limit-up-enabled", 0 );
457                break;
458
459            case 'v':
460                tr_bencDictAddStr( &top, "method", "torrent-verify" );
461                addIdArg( args, id );
462                break;
463
464            case 'V':
465                fprintf( stderr, "Transmission %s\n", LONG_VERSION_STRING );
466                exit( 0 );
467                break;
468
469            case 'w': {
470                char * path = absolutify( optarg );
471                tr_bencDictAddStr( &top, "method", "session-set" );
472                tr_bencDictAddStr( args, "download-dir", path );
473                tr_free( path );
474                break;
475            }
476
477            case 'x':
478                tr_bencDictAddStr( &top, "method", "session-set" );
479                tr_bencDictAddInt( args, "pex-allowed", 1 );
480                break;
481
482            case 'X':
483                tr_bencDictAddStr( &top, "method", "session-set" );
484                tr_bencDictAddInt( args, "pex-allowed", 0 );
485                break;
486
487            case 900:
488                tr_bencDictAddStr( &top, "method", "torrent-set" );
489                addIdArg( args, id );
490                addFiles( args, "priority-high", optarg );
491                break;
492
493            case 901:
494                tr_bencDictAddStr( &top, "method", "torrent-set" );
495                addIdArg( args, id );
496                addFiles( args, "priority-normal", optarg );
497                break;
498
499            case 902:
500                tr_bencDictAddStr( &top, "method", "torrent-set" );
501                addIdArg( args, id );
502                addFiles( args, "priority-low", optarg );
503                break;
504
505            case 910:
506                tr_bencDictAddStr( &top, "method", "session-set" );
507                tr_bencDictAddStr( args, "encryption", "required" );
508                break;
509
510            case 911:
511                tr_bencDictAddStr( &top, "method", "session-set" );
512                tr_bencDictAddStr( args, "encryption", "preferred" );
513                break;
514
515            case 912:
516                tr_bencDictAddStr( &top, "method", "session-set" );
517                tr_bencDictAddStr( args, "encryption", "tolerated" );
518                break;
519
520            case 920:
521                tr_bencDictAddStr( &top, "method", "session-get" );
522                tr_bencDictAddInt( &top, "tag", TAG_SESSION );
523                break;
524
525            case 930:
526                tr_bencDictAddStr( &top, "method", "torrent-set" );
527                addIdArg( args, id );
528                tr_bencDictAddInt( args, "peer-limit", atoi(optarg) );
529                break;
530
531            case 931:
532                tr_bencDictAddStr( &top, "method", "session-set" );
533                tr_bencDictAddInt( args, "peer-limit", atoi(optarg) );
534                break;
535
536            case 940:
537                tr_bencDictAddStr( &top, "method", "torrent-get" );
538                tr_bencDictAddInt( &top, "tag", TAG_PEERS );
539                addIdArg( args, id );
540                fields = tr_bencDictAddList( args, "fields", 1 );
541                tr_bencListAddStr( fields, "peers" );
542                break;
543
544            case 950:
545                tr_bencDictAddStr( &top, "method", "torrent-set" );
546                tr_bencDictAddDouble( args, "ratio-limit", atof(optarg) );
547                tr_bencDictAddInt( args, "ratio-limit-mode", TR_RATIOLIMIT_SINGLE );
548                addIdArg( args, id );
549                break;
550
551            case 951:
552                tr_bencDictAddStr( &top, "method", "torrent-set" );
553                tr_bencDictAddInt( args, "ratio-limit-mode", TR_RATIOLIMIT_GLOBAL );
554                addIdArg( args, id );
555                break;
556
557            case 952:
558                tr_bencDictAddStr( &top, "method", "torrent-set" );
559                tr_bencDictAddInt( args, "ratio-limit-mode", TR_RATIOLIMIT_UNLIMITED );
560                addIdArg( args, id );
561                break;
562
563            case 953:
564                tr_bencDictAddStr( &top, "method", "session-set" );
565                tr_bencDictAddDouble( args, "ratio-limit", atof(optarg) );
566                tr_bencDictAddInt( args, "ratio-limit-enabled", 1 );
567                break;
568
569            case 954:
570                tr_bencDictAddStr( &top, "method", "session-set" );
571                tr_bencDictAddInt( args, "ratio-limit-enabled", 0 );
572                break;
573
574            case TR_OPT_ERR:
575                fprintf( stderr, "invalid option\n" );
576                showUsage( );
577                break;
578
579            default:
580                fprintf( stderr, "got opt [%d]\n", (int)c );
581                showUsage( );
582                break;
583        }
584
585        if( addArg )
586        {
587            struct evbuffer * buf = tr_getBuffer( );
588            reqs[reqCount++] = tr_strdup( tr_bencSaveAsJSON( &top, buf ) );
589            tr_releaseBuffer( buf );
590        }
591
592        tr_bencFree( &top );
593    }
594}
595
596/* [host:port] or [host] or [port] */
597static void
598getHostAndPort( int *   argc,
599                char ** argv,
600                char ** host,
601                int *   port )
602{
603    if( *argv[1] != '-' )
604    {
605        int          i;
606        const char * s = argv[1];
607        const char * delim = strchr( s, ':' );
608        if( delim )   /* user passed in both host and port */
609        {
610            *host = tr_strndup( s, delim - s );
611            *port = atoi( delim + 1 );
612        }
613        else
614        {
615            char *    end;
616            const int i = strtol( s, &end, 10 );
617            if( !*end ) /* user passed in a port */
618                *port = i;
619            else /* user passed in a host */
620                *host = tr_strdup( s );
621        }
622
623        *argc -= 1;
624        for( i = 1; i < *argc; ++i )
625            argv[i] = argv[i + 1];
626    }
627}
628
629static size_t
630writeFunc( void * ptr,
631           size_t size,
632           size_t nmemb,
633           void * buf )
634{
635    const size_t byteCount = size * nmemb;
636
637    evbuffer_add( buf, ptr, byteCount );
638    return byteCount;
639}
640
641static void
642etaToString( char *  buf,
643             size_t  buflen,
644             int64_t eta )
645{
646    if( eta < 0 ) tr_snprintf( buf, buflen, "Unknown" );
647    else if( eta < 60 ) tr_snprintf( buf, buflen, "%" PRId64 "sec", eta );
648    else if( eta <
649            ( 60 * 60 ) ) tr_snprintf( buf, buflen, "%" PRId64 " min",
650                                       eta / 60 );
651    else if( eta <
652            ( 60 * 60 * 24 ) ) tr_snprintf( buf, buflen, "%" PRId64 " hrs",
653                                           eta / ( 60 * 60 ) );
654    else tr_snprintf( buf, buflen, "%" PRId64 " days", eta / ( 60 * 60 * 24 ) );
655}
656
657#define KILOBYTE_FACTOR 1024.0
658#define MEGABYTE_FACTOR ( 1024.0 * 1024.0 )
659#define GIGABYTE_FACTOR ( 1024.0 * 1024.0 * 1024.0 )
660
661static char*
662strlratio2( char * buf, double ratio, size_t buflen )
663{
664    return tr_strratio( buf, buflen, ratio, "Inf" );
665}
666
667static char*
668strlratio( char * buf,
669           double numerator,
670           double denominator,
671           size_t buflen )
672{
673    double ratio;
674
675    if( denominator )
676        ratio = numerator / denominator;
677    else if( numerator )
678        ratio = TR_RATIO_INF;
679    else
680        ratio = TR_RATIO_NA;
681
682    return strlratio2( buf, ratio, buflen );
683}
684
685static char*
686strlsize( char *  buf, int64_t size, size_t  buflen )
687{
688    if( !size )
689        tr_strlcpy( buf, "None", buflen );
690    else if( size < (int64_t)KILOBYTE_FACTOR )
691        tr_snprintf( buf, buflen, "%'" PRId64 " bytes", (int64_t)size );
692    else
693    {
694        double displayed_size;
695        if( size < (int64_t)MEGABYTE_FACTOR )
696        {
697            displayed_size = (double) size / KILOBYTE_FACTOR;
698            tr_snprintf( buf, buflen, "%'.1f KB", displayed_size );
699        }
700        else if( size < (int64_t)GIGABYTE_FACTOR )
701        {
702            displayed_size = (double) size / MEGABYTE_FACTOR;
703            tr_snprintf( buf, buflen, "%'.1f MB", displayed_size );
704        }
705        else
706        {
707            displayed_size = (double) size / GIGABYTE_FACTOR;
708            tr_snprintf( buf, buflen, "%'.1f GB", displayed_size );
709        }
710    }
711    return buf;
712}
713
714static char*
715getStatusString( tr_benc * t, char * buf, size_t buflen )
716{
717    int64_t status;
718
719    if( !tr_bencDictFindInt( t, "status", &status ) )
720    {
721        *buf = '\0';
722    }
723    else switch( status )
724    {
725        case TR_STATUS_STOPPED:
726            tr_strlcpy( buf, "Stopped", buflen );
727            break;
728
729        case TR_STATUS_CHECK_WAIT:
730        case TR_STATUS_CHECK: {
731            const char * str = status == TR_STATUS_CHECK_WAIT
732                             ? "Will Verify"
733                             : "Verifying";
734            double percent;
735            if( tr_bencDictFindDouble( t, "recheckProgress", &percent ) )
736                tr_snprintf( buf, buflen, "%s (%.0f%%)", str, percent*100.0 );
737            else
738                tr_strlcpy( buf, str, buflen );
739
740            break;
741        }
742
743        case TR_STATUS_DOWNLOAD:
744        case TR_STATUS_SEED: {
745            int64_t fromUs = 0; 
746            int64_t toUs = 0; 
747            tr_bencDictFindInt( t, "peersGettingFromUs", &fromUs ); 
748            tr_bencDictFindInt( t, "peersSendingToUs", &toUs ); 
749            if( fromUs && toUs ) 
750                tr_strlcpy( buf, "Up & Down", buflen ); 
751            else if( toUs ) 
752                tr_strlcpy( buf, "Downloading", buflen ); 
753            else if( fromUs ) { 
754                int64_t leftUntilDone = 0; 
755                tr_bencDictFindInt( t, "leftUntilDone", &leftUntilDone ); 
756                if( leftUntilDone > 0 )
757                    tr_strlcpy( buf, "Uploading", buflen ); 
758                else
759                    tr_strlcpy( buf, "Seeding", buflen ); 
760            } else {
761                tr_strlcpy( buf, "Idle", buflen ); 
762            }
763            break;
764        }
765    }
766
767    return buf;
768}
769
770static const char*
771getTrackerDateStr( const time_t t, tr_bool isStopped )
772{
773    const char * str;
774    switch( t ) {
775        case 0: str = isStopped ? "None (Stopped)\n" : "None\n"; break;
776        case 1: str = "In Progress\n"; break;
777        default: str = ctime( &t ); break;
778    }
779    return str;
780}
781
782static void
783printSession( tr_benc * top )
784{
785    tr_benc *args;
786    if( ( tr_bencDictFindDict( top, "arguments", &args ) ) )
787    {
788        const char * str;
789        int64_t      i;
790
791        printf( "VERSION\n" );
792        if( tr_bencDictFindStr( args,  "version", &str ) )
793            printf( "  Daemon version: %s\n", str );
794        if( tr_bencDictFindInt( args, "rpc-version", &i ) )
795            printf( "  RPC version: %" PRId64 "\n", i );
796        if( tr_bencDictFindInt( args, "rpc-version-minimum", &i ) )
797            printf( "  RPC minimum version: %" PRId64 "\n", i );
798        printf( "\n" );
799
800        printf( "TRANSFER\n" );
801        if( tr_bencDictFindStr( args,  "download-dir", &str ) )
802            printf( "  Download directory: %s\n", str );
803        if( tr_bencDictFindInt( args, "port", &i ) )
804            printf( "  Listenport: %" PRId64 "\n", i );
805        if( tr_bencDictFindInt( args, "port-forwarding-enabled", &i ) )
806            printf( "  Portforwarding enabled: %s\n", ( i ? "Yes" : "No" ) );
807        if( tr_bencDictFindInt( args, "pex-allowed", &i ) )
808            printf( "  Peer exchange allowed: %s\n", ( i ? "Yes" : "No" ) );
809        if( tr_bencDictFindStr( args,  "encryption", &str ) )
810            printf( "  Encryption: %s\n", str );
811        printf( "\n" );
812
813        printf( "LIMITS\n" );
814        if( tr_bencDictFindInt( args, "peer-limit", &i ) )
815            printf( "  Peer limit: %" PRId64 "\n", i );
816        if( tr_bencDictFindInt( args, "speed-limit-down-enabled", &i ) )
817            printf( "  Downloadlimit enabled: %s\n", ( i ? "Yes" : "No" ) );
818        if( tr_bencDictFindInt( args, "speed-limit-down", &i ) )
819            printf( "  Downloadlimit: %6" PRId64 " KB/sec\n", i );
820        if( tr_bencDictFindInt( args, "speed-limit-up-enabled", &i ) )
821            printf( "  Uploadlimit enabled:   %s\n", ( i ? "Yes" : "No" ) );
822        if( tr_bencDictFindInt( args, "speed-limit-up", &i ) )
823            printf( "  Uploadlimit:   %6" PRId64 " KB/sec\n", i );
824    }
825}
826
827static void
828printDetails( tr_benc * top )
829{
830    tr_benc *args, *torrents;
831
832    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
833      && ( tr_bencDictFindList( args, "torrents", &torrents ) ) )
834    {
835        int ti, tCount;
836        for( ti = 0, tCount = tr_bencListSize( torrents ); ti < tCount;
837             ++ti )
838        {
839            tr_benc *    t = tr_bencListChild( torrents, ti );
840            tr_benc *    l;
841            const uint8_t * raw;
842            size_t       rawlen;
843            const char * str;
844            char         buf[512];
845            char         buf2[512];
846            int64_t      i, j, k;
847            tr_bool      isStopped;
848
849            isStopped = tr_bencDictFindInt( t, "status", &i ) && (i==TR_STATUS_STOPPED);
850
851            printf( "NAME\n" );
852            if( tr_bencDictFindInt( t, "id", &i ) )
853                printf( "  Id: %" PRId64 "\n", i );
854            if( tr_bencDictFindStr( t, "name", &str ) )
855                printf( "  Name: %s\n", str );
856            if( tr_bencDictFindStr( t, "hashString", &str ) )
857                printf( "  Hash: %s\n", str );
858            printf( "\n" );
859
860            printf( "TRANSFER\n" );
861            getStatusString( t, buf, sizeof( buf ) );
862            printf( "  State: %s\n", buf );
863
864            if( tr_bencDictFindStr( t, "downloadDir", &str ) )
865                printf( "  Location: %s\n", str );
866
867            if( tr_bencDictFindInt( t, "sizeWhenDone", &i )
868              && tr_bencDictFindInt( t, "leftUntilDone", &j ) )
869            {
870                strlratio( buf, 100.0 * ( i - j ), i, sizeof( buf ) );
871                printf( "  Percent Done: %s%%\n", buf );
872            }
873
874            if( tr_bencDictFindInt( t, "eta", &i ) )
875            {
876                etaToString( buf, sizeof( buf ), i );
877                printf( "  ETA: %s\n", buf );
878            }
879            if( tr_bencDictFindInt( t, "rateDownload", &i ) )
880                printf( "  Download Speed: %.1f KB/s\n", i / 1024.0 );
881            if( tr_bencDictFindInt( t, "rateUpload", &i ) )
882                printf( "  Upload Speed: %.1f KB/s\n", i / 1024.0 );
883            if( tr_bencDictFindInt( t, "haveUnchecked", &i )
884              && tr_bencDictFindInt( t, "haveValid", &j ) )
885            {
886                strlsize( buf, i + j, sizeof( buf ) );
887                strlsize( buf2, j, sizeof( buf2 ) );
888                printf( "  Have: %s (%s verified)\n", buf, buf2 );
889            }
890
891            if( tr_bencDictFindInt( t, "sizeWhenDone", &i )
892              && tr_bencDictFindInt( t, "totalSize", &j ) )
893            {
894                strlsize( buf, j, sizeof( buf ) );
895                strlsize( buf2, i, sizeof( buf2 ) );
896                printf( "  Total size: %s (%s wanted)\n", buf, buf2 );
897            }
898            if( tr_bencDictFindInt( t, "downloadedEver", &i )
899              && tr_bencDictFindInt( t, "uploadedEver", &j ) )
900            {
901                strlsize( buf, i, sizeof( buf ) );
902                printf( "  Downloaded: %s\n", buf );
903                strlsize( buf, j, sizeof( buf ) );
904                printf( "  Uploaded: %s\n", buf );
905                strlratio( buf, j, i, sizeof( buf ) );
906                printf( "  Ratio: %s\n", buf );
907            }
908            if( tr_bencDictFindInt( t, "corruptEver", &i ) )
909            {
910                strlsize( buf, i, sizeof( buf ) );
911                printf( "  Corrupt DL: %s\n", buf );
912            }
913            if( tr_bencDictFindStr( t, "errorString", &str ) && str && *str )
914                printf( "  Error: %s\n", str );
915
916            if( tr_bencDictFindInt( t, "peersConnected", &i )
917              && tr_bencDictFindInt( t, "peersGettingFromUs", &j )
918              && tr_bencDictFindInt( t, "peersSendingToUs", &k ) )
919            {
920                printf(
921                    "  Peers: "
922                    "connected to %" PRId64 ", "
923                                            "uploading to %" PRId64
924                    ", "
925                    "downloading from %"
926                    PRId64 "\n",
927                    i, j, k );
928            }
929
930            if( tr_bencDictFindList( t, "webseeds", &l )
931              && tr_bencDictFindInt( t, "webseedsSendingToUs", &i ) )
932            {
933                const int64_t n = tr_bencListSize( l );
934                if( n > 0 )
935                    printf(
936                        "  Web Seeds: downloading from %" PRId64 " of %"
937                        PRId64
938                        " web seeds\n", i, n );
939            }
940            printf( "\n" );
941
942            printf( "HISTORY\n" );
943            if( tr_bencDictFindInt( t, "addedDate", &i ) && i )
944            {
945                const time_t tt = i;
946                printf( "  Date added:      %s", ctime( &tt ) );
947            }
948            if( tr_bencDictFindInt( t, "doneDate", &i ) && i )
949            {
950                const time_t tt = i;
951                printf( "  Date finished:   %s", ctime( &tt ) );
952            }
953            if( tr_bencDictFindInt( t, "startDate", &i ) && i )
954            {
955                const time_t tt = i;
956                printf( "  Date started:    %s", ctime( &tt ) );
957            }
958            if( tr_bencDictFindInt( t, "activityDate", &i ) && i )
959            {
960                const time_t tt = i;
961                printf( "  Latest activity: %s", ctime( &tt ) );
962            }
963            printf( "\n" );
964
965            printf( "TRACKER\n" );
966            if( tr_bencDictFindInt( t, "lastAnnounceTime", &i ) )
967                printf( "  Latest announce: %s", getTrackerDateStr( (time_t)i, isStopped ) );
968            if( tr_bencDictFindStr( t, "announceURL", &str ) )
969                printf( "  Announce URL: %s\n", str );
970            if( tr_bencDictFindStr( t, "announceResponse", &str ) && str && *str )
971                printf( "  Announce response: %s\n", str );
972            if( tr_bencDictFindInt( t, "nextAnnounceTime", &i ) )
973                printf( "  Next announce:   %s", getTrackerDateStr( (time_t)i, isStopped ) );
974            if( tr_bencDictFindInt( t, "lastScrapeTime", &i ) )
975                printf( "  Latest scrape:   %s", getTrackerDateStr( (time_t)i, isStopped ) );
976            if( tr_bencDictFindStr( t, "scrapeResponse", &str ) )
977                printf( "  Scrape response: %s\n", str );
978            if( tr_bencDictFindInt( t, "nextScrapeTime", &i ) )
979                printf( "  Next scrape:     %s", getTrackerDateStr( (time_t)i, isStopped ) );
980            if( tr_bencDictFindInt( t, "seeders", &i ) && tr_bencDictFindInt( t, "leechers", &j ) )
981                printf( "  Tracker knows of %" PRId64 " seeders and %" PRId64 " leechers\n", i, j );
982            if( tr_bencDictFindInt( t, "timesCompleted", &i ) )
983                printf( "  Tracker has seen %" PRId64 " clients complete this torrent\n", i );
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_bencDictFindInt( t, "isPrivate", &i ) )
993                printf( "  Public torrent: %s\n", ( i ? "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: %" PRId64 "\n", i );
1002            printf( "\n" );
1003
1004            printf("PIECES\n  ");
1005            if( tr_bencDictFindRaw( t, "pieces", &raw, &rawlen ) && tr_bencDictFindInt( t, "pieceCount", &j ) ) {
1006                int len;
1007                char * str = tr_base64_decode( raw, rawlen, &len );
1008                for( i=k=0; k<len; ++k ) {
1009                    int e;
1010                    for( e=0; i<j && e<8; ++e, ++i )
1011                        printf( str[k] & (1<<(7-e)) ? "1" : "0" );
1012                    printf( " " );
1013                    if( !(i%64) )
1014                        printf( "\n  " );
1015                }
1016                tr_free( str );
1017            }
1018            printf( "\n" );
1019        }
1020    }
1021}
1022
1023static void
1024printFileList( tr_benc * top )
1025{
1026    tr_benc *args, *torrents;
1027
1028    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
1029      && ( tr_bencDictFindList( args, "torrents", &torrents ) ) )
1030    {
1031        int i, in;
1032        for( i = 0, in = tr_bencListSize( torrents ); i < in; ++i )
1033        {
1034            tr_benc *    d = tr_bencListChild( torrents, i );
1035            tr_benc *    files, *priorities, *wanteds;
1036            const char * name;
1037            if( tr_bencDictFindStr( d, "name", &name )
1038              && tr_bencDictFindList( d, "files", &files )
1039              && tr_bencDictFindList( d, "priorities", &priorities )
1040              && tr_bencDictFindList( d, "wanted", &wanteds ) )
1041            {
1042                int j = 0, jn = tr_bencListSize( files );
1043                printf( "%s (%d files):\n", name, jn );
1044                printf( "%3s  %4s %8s %3s %9s  %s\n", "#", "Done",
1045                        "Priority", "Get", "Size",
1046                        "Name" );
1047                for( j = 0, jn = tr_bencListSize( files ); j < jn; ++j )
1048                {
1049                    int64_t      have;
1050                    int64_t      length;
1051                    int64_t      priority;
1052                    int64_t      wanted;
1053                    const char * filename;
1054                    tr_benc *    file = tr_bencListChild( files, j );
1055                    if( tr_bencDictFindInt( file, "length", &length )
1056                      && tr_bencDictFindStr( file, "name", &filename )
1057                      && tr_bencDictFindInt( file, "bytesCompleted", &have )
1058                      && tr_bencGetInt( tr_bencListChild( priorities,
1059                                                          j ), &priority )
1060                      && tr_bencGetInt( tr_bencListChild( wanteds,
1061                                                          j ), &wanted ) )
1062                    {
1063                        char         sizestr[64];
1064                        double       percent = (double)have / length;
1065                        const char * pristr;
1066                        strlsize( sizestr, length, sizeof( sizestr ) );
1067                        switch( priority )
1068                        {
1069                            case TR_PRI_LOW:
1070                                pristr = "Low"; break;
1071
1072                            case TR_PRI_HIGH:
1073                                pristr = "High"; break;
1074
1075                            default:
1076                                pristr = "Normal"; break;
1077                        }
1078                        printf( "%3d: %3.0f%% %-8s %-3s %9s  %s\n",
1079                                j,
1080                                ( 100.0 * percent ),
1081                                pristr,
1082                                ( wanted ? "Yes" : "No" ),
1083                                sizestr,
1084                                filename );
1085                    }
1086                }
1087            }
1088        }
1089    }
1090}
1091
1092static void
1093printPeersImpl( tr_benc * peers )
1094{
1095    int i, n;
1096    printf( "%-20s  %-12s  %-5s %-6s  %-6s  %s\n",
1097            "Address", "Flags", "Done", "Down", "Up", "Client" );
1098    for( i = 0, n = tr_bencListSize( peers ); i < n; ++i )
1099    {
1100        double progress;
1101        const char * address, * client, * flagstr;
1102        int64_t rateToClient, rateToPeer;
1103        tr_benc * d = tr_bencListChild( peers, i );
1104
1105        if( tr_bencDictFindStr( d, "address", &address )
1106          && tr_bencDictFindStr( d, "clientName", &client )
1107          && tr_bencDictFindDouble( d, "progress", &progress )
1108          && tr_bencDictFindStr( d, "flagStr", &flagstr )
1109          && tr_bencDictFindInt( d, "rateToClient", &rateToClient )
1110          && tr_bencDictFindInt( d, "rateToPeer", &rateToPeer ) )
1111        {
1112            printf( "%-20s  %-12s  %-5.1f %6.1f  %6.1f  %s\n",
1113                    address, flagstr, (progress*100.0),
1114                    rateToClient / 1024.0,
1115                    rateToPeer / 1024.0,
1116                    client );
1117        }
1118    }
1119}
1120
1121static void
1122printPeers( tr_benc * top )
1123{
1124    tr_benc *args, *torrents;
1125
1126    if( tr_bencDictFindDict( top, "arguments", &args )
1127      && tr_bencDictFindList( args, "torrents", &torrents ) )
1128    {
1129        int i, n;
1130        for( i=0, n=tr_bencListSize( torrents ); i<n; ++i )
1131        {
1132            tr_benc * peers;
1133            tr_benc * torrent = tr_bencListChild( torrents, i );
1134            if( tr_bencDictFindList( torrent, "peers", &peers ) ) {
1135                printPeersImpl( peers );
1136                if( i+1<n )
1137                    printf( "\n" );
1138            }
1139        }
1140    }
1141}
1142
1143static void
1144printTorrentList( tr_benc * top )
1145{
1146    tr_benc *args, *list;
1147
1148    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
1149      && ( tr_bencDictFindList( args, "torrents", &list ) ) )
1150    {
1151        int i, n;
1152        int64_t total_up = 0, total_down = 0, total_size = 0;
1153        char haveStr[32];
1154
1155        printf( "%-4s   %-4s  %9s  %-8s  %6s  %6s  %-5s  %-11s  %s\n",
1156                "ID", "Done", "Have", "ETA", "Up", "Down", "Ratio", "Status",
1157                "Name" );
1158
1159        for( i = 0, n = tr_bencListSize( list ); i < n; ++i )
1160        {
1161            int64_t      id, eta, status, up, down;
1162            int64_t      sizeWhenDone, leftUntilDone;
1163            double       ratio;
1164            const char * name;
1165            tr_benc *   d = tr_bencListChild( list, i );
1166            if( tr_bencDictFindInt( d, "eta", &eta )
1167              && tr_bencDictFindInt( d, "id", &id )
1168              && tr_bencDictFindInt( d, "leftUntilDone", &leftUntilDone )
1169              && tr_bencDictFindStr( d, "name", &name )
1170              && tr_bencDictFindInt( d, "rateDownload", &down )
1171              && tr_bencDictFindInt( d, "rateUpload", &up )
1172              && tr_bencDictFindInt( d, "sizeWhenDone", &sizeWhenDone )
1173              && tr_bencDictFindInt( d, "status", &status )
1174              && tr_bencDictFindDouble( d, "uploadRatio", &ratio ) )
1175            {
1176                char etaStr[16];
1177                char statusStr[64];
1178                char ratioStr[32];
1179                char doneStr[8];
1180                int64_t error;
1181                char errorMark;
1182
1183                if( sizeWhenDone )
1184                    tr_snprintf( doneStr, sizeof( doneStr ), "%d%%", (int)( 100.0 * ( sizeWhenDone - leftUntilDone ) / sizeWhenDone ) );
1185                else
1186                    tr_strlcpy( doneStr, "n/a", sizeof( doneStr ) );
1187
1188                strlsize( haveStr, sizeWhenDone - leftUntilDone, sizeof( haveStr ) );
1189
1190                if( leftUntilDone )
1191                    etaToString( etaStr, sizeof( etaStr ), eta );
1192                else
1193                    tr_snprintf( etaStr, sizeof( etaStr ), "Done" );
1194                if( tr_bencDictFindInt( d, "error", &error ) && error )
1195                    errorMark = '*';
1196                else
1197                    errorMark = ' ';
1198                printf(
1199                    "%4d%c  %4s  %9s  %-8s  %6.1f  %6.1f  %5s  %-11s  %s\n",
1200                    (int)id, errorMark,
1201                    doneStr,
1202                    haveStr,
1203                    etaStr,
1204                    up / 1024.0,
1205                    down / 1024.0,
1206                    strlratio2( ratioStr, ratio, sizeof( ratioStr ) ),
1207                    getStatusString( d, statusStr, sizeof( statusStr ) ),
1208                    name );
1209
1210                total_up += up;
1211                total_down += down;
1212                total_size += sizeWhenDone - leftUntilDone;
1213            }
1214        }
1215
1216        printf( "Sum:         %9s            %6.1f  %6.1f\n",
1217                strlsize( haveStr, total_size, sizeof( haveStr ) ),
1218                total_up / 1024.0,
1219                total_down / 1024.0 );
1220    }
1221}
1222
1223static void
1224processResponse( const char * host,
1225                 int          port,
1226                 const void * response,
1227                 size_t       len )
1228{
1229    tr_benc top;
1230
1231    if( debug )
1232        fprintf( stderr, "got response:\n--------\n%*.*s\n--------\n",
1233                 (int)len, (int)len, (const char*) response );
1234
1235    if( tr_jsonParse( response, len, &top, NULL ) )
1236        tr_nerr( MY_NAME, "Unable to parse response \"%*.*s\"", (int)len,
1237                 (int)len, (char*)response );
1238    else
1239    {
1240        int64_t      tag = -1;
1241        const char * str;
1242        tr_bencDictFindInt( &top, "tag", &tag );
1243
1244        switch( tag )
1245        {
1246            case TAG_SESSION:
1247                printSession( &top ); break;
1248
1249            case TAG_FILES:
1250                printFileList( &top ); break;
1251
1252            case TAG_DETAILS:
1253                printDetails( &top ); break;
1254
1255            case TAG_LIST:
1256                printTorrentList( &top ); break;
1257
1258            case TAG_PEERS:
1259                printPeers( &top ); break;
1260
1261            default:
1262                if( tr_bencDictFindStr( &top, "result", &str ) )
1263                    printf( "%s:%d responded: \"%s\"\n", host, port, str );
1264        }
1265
1266        tr_bencFree( &top );
1267    }
1268}
1269
1270static void
1271processRequests( const char *  host,
1272                 int           port,
1273                 const char ** reqs,
1274                 int           reqCount )
1275{
1276    int               i;
1277    CURL *            curl;
1278    struct evbuffer * buf = evbuffer_new( );
1279    char *            url = tr_strdup_printf(
1280        "http://%s:%d/transmission/rpc", host, port );
1281
1282    curl = curl_easy_init( );
1283    curl_easy_setopt( curl, CURLOPT_VERBOSE, debug );
1284#ifdef HAVE_LIBZ
1285    curl_easy_setopt( curl, CURLOPT_ENCODING, "deflate" );
1286#endif
1287    curl_easy_setopt( curl, CURLOPT_USERAGENT, MY_NAME "/" LONG_VERSION_STRING );
1288    curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, writeFunc );
1289    curl_easy_setopt( curl, CURLOPT_WRITEDATA, buf );
1290    curl_easy_setopt( curl, CURLOPT_POST, 1 );
1291    curl_easy_setopt( curl, CURLOPT_URL, url );
1292    curl_easy_setopt( curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL );
1293    curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
1294    if( netrc )
1295        curl_easy_setopt( curl, CURLOPT_NETRC_FILE, netrc );
1296    if( auth )
1297        curl_easy_setopt( curl, CURLOPT_USERPWD, auth );
1298
1299    for( i = 0; i < reqCount; ++i )
1300    {
1301        CURLcode res;
1302        curl_easy_setopt( curl, CURLOPT_POSTFIELDS, reqs[i] );
1303        if( debug )
1304            fprintf( stderr, "posting:\n--------\n%s\n--------\n", reqs[i] );
1305        if( ( res = curl_easy_perform( curl ) ) )
1306            tr_nerr( MY_NAME, "(%s:%d) %s", host, port,
1307                    curl_easy_strerror( res ) );
1308        else
1309            processResponse( host, port, EVBUFFER_DATA(
1310                                buf ), EVBUFFER_LENGTH( buf ) );
1311
1312        evbuffer_drain( buf, EVBUFFER_LENGTH( buf ) );
1313    }
1314
1315    /* cleanup */
1316    tr_free( url );
1317    evbuffer_free( buf );
1318    curl_easy_cleanup( curl );
1319}
1320
1321int
1322main( int     argc,
1323      char ** argv )
1324{
1325    int    i;
1326    int    port = DEFAULT_PORT;
1327    char * host = NULL;
1328
1329    if( argc < 2 )
1330        showUsage( );
1331
1332    getHostAndPort( &argc, argv, &host, &port );
1333    if( host == NULL )
1334        host = tr_strdup( DEFAULT_HOST );
1335
1336    readargs( argc, (const char**)argv );
1337    if( reqCount )
1338        processRequests( host, port, (const char**)reqs, reqCount );
1339    else
1340        showUsage( );
1341
1342    for( i = 0; i < reqCount; ++i )
1343        tr_free( reqs[i] );
1344
1345    tr_free( host );
1346    return 0;
1347}
1348
Note: See TracBrowser for help on using the repository browser.