source: trunk/daemon/remote.c @ 7992

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

(trunk) #1608: Completed pieces bitfield in rpc

  • Property svn:keywords set to Date Rev Author Id
File size: 45.7 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 7992 2009-03-02 05:48:32Z 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    if( (int)ratio == TR_RATIO_NA )
665        tr_strlcpy( buf, "None", buflen );
666    else if( (int)ratio == TR_RATIO_INF )
667        tr_strlcpy( buf, "Inf", buflen );
668    else if( ratio < 10.0 )
669        tr_snprintf( buf, buflen, "%'.2f", ratio );
670    else if( ratio < 100.0 )
671        tr_snprintf( buf, buflen, "%'.1f", ratio );
672    else
673        tr_snprintf( buf, buflen, "%'.0f", ratio );
674    return buf;
675}
676
677static char*
678strlratio( char * buf,
679           double numerator,
680           double denominator,
681           size_t buflen )
682{
683    double ratio;
684
685    if( denominator )
686        ratio = numerator / denominator;
687    else if( numerator )
688        ratio = TR_RATIO_INF;
689    else
690        ratio = TR_RATIO_NA;
691
692    return strlratio2( buf, ratio, buflen );
693}
694
695static char*
696strlsize( char *  buf, int64_t size, size_t  buflen )
697{
698    if( !size )
699        tr_strlcpy( buf, "None", buflen );
700    else if( size < (int64_t)KILOBYTE_FACTOR )
701        tr_snprintf( buf, buflen, "%'" PRId64 " bytes", (int64_t)size );
702    else
703    {
704        double displayed_size;
705        if( size < (int64_t)MEGABYTE_FACTOR )
706        {
707            displayed_size = (double) size / KILOBYTE_FACTOR;
708            tr_snprintf( buf, buflen, "%'.1f KB", displayed_size );
709        }
710        else if( size < (int64_t)GIGABYTE_FACTOR )
711        {
712            displayed_size = (double) size / MEGABYTE_FACTOR;
713            tr_snprintf( buf, buflen, "%'.1f MB", displayed_size );
714        }
715        else
716        {
717            displayed_size = (double) size / GIGABYTE_FACTOR;
718            tr_snprintf( buf, buflen, "%'.1f GB", displayed_size );
719        }
720    }
721    return buf;
722}
723
724static char*
725getStatusString( tr_benc * t, char * buf, size_t buflen )
726{
727    int64_t status;
728
729    if( !tr_bencDictFindInt( t, "status", &status ) )
730    {
731        *buf = '\0';
732    }
733    else switch( status )
734    {
735        case TR_STATUS_STOPPED:
736            tr_strlcpy( buf, "Stopped", buflen );
737            break;
738
739        case TR_STATUS_CHECK_WAIT:
740        case TR_STATUS_CHECK: {
741            const char * str = status == TR_STATUS_CHECK_WAIT
742                             ? "Will Verify"
743                             : "Verifying";
744            double percent;
745            if( tr_bencDictFindDouble( t, "recheckProgress", &percent ) )
746                tr_snprintf( buf, buflen, "%s (%.0f%%)", str, percent*100.0 );
747            else
748                tr_strlcpy( buf, str, buflen );
749
750            break;
751        }
752
753        case TR_STATUS_DOWNLOAD:
754        case TR_STATUS_SEED: {
755            int64_t fromUs = 0; 
756            int64_t toUs = 0; 
757            tr_bencDictFindInt( t, "peersGettingFromUs", &fromUs ); 
758            tr_bencDictFindInt( t, "peersSendingToUs", &toUs ); 
759            if( fromUs && toUs ) 
760                tr_strlcpy( buf, "Up & Down", buflen ); 
761            else if( toUs ) 
762                tr_strlcpy( buf, "Downloading", buflen ); 
763            else if( fromUs ) { 
764                int64_t leftUntilDone = 0; 
765                tr_bencDictFindInt( t, "leftUntilDone", &leftUntilDone ); 
766                if( leftUntilDone > 0 )
767                    tr_strlcpy( buf, "Uploading", buflen ); 
768                else
769                    tr_strlcpy( buf, "Seeding", buflen ); 
770            } else {
771                tr_strlcpy( buf, "Idle", buflen ); 
772            }
773            break;
774        }
775    }
776
777    return buf;
778}
779
780static const char*
781getTrackerDateStr( const time_t t, tr_bool isStopped )
782{
783    const char * str;
784    switch( t ) {
785        case 0: str = isStopped ? "None (Stopped)\n" : "None\n"; break;
786        case 1: str = "In Progress\n"; break;
787        default: str = ctime( &t ); break;
788    }
789    return str;
790}
791
792static void
793printSession( tr_benc * top )
794{
795    tr_benc *args;
796    if( ( tr_bencDictFindDict( top, "arguments", &args ) ) )
797    {
798        const char * str;
799        int64_t      i;
800
801        printf( "VERSION\n" );
802        if( tr_bencDictFindStr( args,  "version", &str ) )
803            printf( "  Daemon version: %s\n", str );
804        if( tr_bencDictFindInt( args, "rpc-version", &i ) )
805            printf( "  RPC version: %" PRId64 "\n", i );
806        if( tr_bencDictFindInt( args, "rpc-version-minimum", &i ) )
807            printf( "  RPC minimum version: %" PRId64 "\n", i );
808        printf( "\n" );
809
810        printf( "TRANSFER\n" );
811        if( tr_bencDictFindStr( args,  "download-dir", &str ) )
812            printf( "  Download directory: %s\n", str );
813        if( tr_bencDictFindInt( args, "port", &i ) )
814            printf( "  Listenport: %" PRId64 "\n", i );
815        if( tr_bencDictFindInt( args, "port-forwarding-enabled", &i ) )
816            printf( "  Portforwarding enabled: %s\n", ( i ? "Yes" : "No" ) );
817        if( tr_bencDictFindInt( args, "pex-allowed", &i ) )
818            printf( "  Peer exchange allowed: %s\n", ( i ? "Yes" : "No" ) );
819        if( tr_bencDictFindStr( args,  "encryption", &str ) )
820            printf( "  Encryption: %s\n", str );
821        printf( "\n" );
822
823        printf( "LIMITS\n" );
824        if( tr_bencDictFindInt( args, "peer-limit", &i ) )
825            printf( "  Peer limit: %" PRId64 "\n", i );
826        if( tr_bencDictFindInt( args, "speed-limit-down-enabled", &i ) )
827            printf( "  Downloadlimit enabled: %s\n", ( i ? "Yes" : "No" ) );
828        if( tr_bencDictFindInt( args, "speed-limit-down", &i ) )
829            printf( "  Downloadlimit: %6" PRId64 " KB/sec\n", i );
830        if( tr_bencDictFindInt( args, "speed-limit-up-enabled", &i ) )
831            printf( "  Uploadlimit enabled:   %s\n", ( i ? "Yes" : "No" ) );
832        if( tr_bencDictFindInt( args, "speed-limit-up", &i ) )
833            printf( "  Uploadlimit:   %6" PRId64 " KB/sec\n", i );
834    }
835}
836
837static void
838printDetails( tr_benc * top )
839{
840    tr_benc *args, *torrents;
841
842    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
843      && ( tr_bencDictFindList( args, "torrents", &torrents ) ) )
844    {
845        int ti, tCount;
846        for( ti = 0, tCount = tr_bencListSize( torrents ); ti < tCount;
847             ++ti )
848        {
849            tr_benc *    t = tr_bencListChild( torrents, ti );
850            tr_benc *    l;
851            const uint8_t * raw;
852            size_t       rawlen;
853            const char * str;
854            char         buf[512];
855            char         buf2[512];
856            int64_t      i, j, k;
857            tr_bool      isStopped;
858
859            isStopped = tr_bencDictFindInt( t, "status", &i ) && (i==TR_STATUS_STOPPED);
860
861            printf( "NAME\n" );
862            if( tr_bencDictFindInt( t, "id", &i ) )
863                printf( "  Id: %" PRId64 "\n", i );
864            if( tr_bencDictFindStr( t, "name", &str ) )
865                printf( "  Name: %s\n", str );
866            if( tr_bencDictFindStr( t, "hashString", &str ) )
867                printf( "  Hash: %s\n", str );
868            printf( "\n" );
869
870            printf( "TRANSFER\n" );
871            getStatusString( t, buf, sizeof( buf ) );
872            printf( "  State: %s\n", buf );
873
874            if( tr_bencDictFindStr( t, "downloadDir", &str ) )
875                printf( "  Location: %s\n", str );
876
877            if( tr_bencDictFindInt( t, "sizeWhenDone", &i )
878              && tr_bencDictFindInt( t, "leftUntilDone", &j ) )
879            {
880                strlratio( buf, 100.0 * ( i - j ), i, sizeof( buf ) );
881                printf( "  Percent Done: %s%%\n", buf );
882            }
883
884            if( tr_bencDictFindInt( t, "eta", &i ) )
885            {
886                etaToString( buf, sizeof( buf ), i );
887                printf( "  ETA: %s\n", buf );
888            }
889            if( tr_bencDictFindInt( t, "rateDownload", &i ) )
890                printf( "  Download Speed: %.1f KB/s\n", i / 1024.0 );
891            if( tr_bencDictFindInt( t, "rateUpload", &i ) )
892                printf( "  Upload Speed: %.1f KB/s\n", i / 1024.0 );
893            if( tr_bencDictFindInt( t, "haveUnchecked", &i )
894              && tr_bencDictFindInt( t, "haveValid", &j ) )
895            {
896                strlsize( buf, i + j, sizeof( buf ) );
897                strlsize( buf2, j, sizeof( buf2 ) );
898                printf( "  Have: %s (%s verified)\n", buf, buf2 );
899            }
900
901            if( tr_bencDictFindInt( t, "sizeWhenDone", &i )
902              && tr_bencDictFindInt( t, "totalSize", &j ) )
903            {
904                strlsize( buf, j, sizeof( buf ) );
905                strlsize( buf2, i, sizeof( buf2 ) );
906                printf( "  Total size: %s (%s wanted)\n", buf, buf2 );
907            }
908            if( tr_bencDictFindInt( t, "downloadedEver", &i )
909              && tr_bencDictFindInt( t, "uploadedEver", &j ) )
910            {
911                strlsize( buf, i, sizeof( buf ) );
912                printf( "  Downloaded: %s\n", buf );
913                strlsize( buf, j, sizeof( buf ) );
914                printf( "  Uploaded: %s\n", buf );
915                strlratio( buf, j, i, sizeof( buf ) );
916                printf( "  Ratio: %s\n", buf );
917            }
918            if( tr_bencDictFindInt( t, "corruptEver", &i ) )
919            {
920                strlsize( buf, i, sizeof( buf ) );
921                printf( "  Corrupt DL: %s\n", buf );
922            }
923            if( tr_bencDictFindStr( t, "errorString", &str ) && str && *str )
924                printf( "  Error: %s\n", str );
925
926            if( tr_bencDictFindInt( t, "peersConnected", &i )
927              && tr_bencDictFindInt( t, "peersGettingFromUs", &j )
928              && tr_bencDictFindInt( t, "peersSendingToUs", &k ) )
929            {
930                printf(
931                    "  Peers: "
932                    "connected to %" PRId64 ", "
933                                            "uploading to %" PRId64
934                    ", "
935                    "downloading from %"
936                    PRId64 "\n",
937                    i, j, k );
938            }
939
940            if( tr_bencDictFindList( t, "webseeds", &l )
941              && tr_bencDictFindInt( t, "webseedsSendingToUs", &i ) )
942            {
943                const int64_t n = tr_bencListSize( l );
944                if( n > 0 )
945                    printf(
946                        "  Web Seeds: downloading from %" PRId64 " of %"
947                        PRId64
948                        " web seeds\n", i, n );
949            }
950            printf( "\n" );
951
952            printf( "HISTORY\n" );
953            if( tr_bencDictFindInt( t, "addedDate", &i ) && i )
954            {
955                const time_t tt = i;
956                printf( "  Date added:      %s", ctime( &tt ) );
957            }
958            if( tr_bencDictFindInt( t, "doneDate", &i ) && i )
959            {
960                const time_t tt = i;
961                printf( "  Date finished:   %s", ctime( &tt ) );
962            }
963            if( tr_bencDictFindInt( t, "startDate", &i ) && i )
964            {
965                const time_t tt = i;
966                printf( "  Date started:    %s", ctime( &tt ) );
967            }
968            if( tr_bencDictFindInt( t, "activityDate", &i ) && i )
969            {
970                const time_t tt = i;
971                printf( "  Latest activity: %s", ctime( &tt ) );
972            }
973            printf( "\n" );
974
975            printf( "TRACKER\n" );
976            if( tr_bencDictFindInt( t, "lastAnnounceTime", &i ) )
977                printf( "  Latest announce: %s", getTrackerDateStr( (time_t)i, isStopped ) );
978            if( tr_bencDictFindStr( t, "announceURL", &str ) )
979                printf( "  Announce URL: %s\n", str );
980            if( tr_bencDictFindStr( t, "announceResponse", &str ) && str && *str )
981                printf( "  Announce response: %s\n", str );
982            if( tr_bencDictFindInt( t, "nextAnnounceTime", &i ) )
983                printf( "  Next announce:   %s", getTrackerDateStr( (time_t)i, isStopped ) );
984            if( tr_bencDictFindInt( t, "lastScrapeTime", &i ) )
985                printf( "  Latest scrape:   %s", getTrackerDateStr( (time_t)i, isStopped ) );
986            if( tr_bencDictFindStr( t, "scrapeResponse", &str ) )
987                printf( "  Scrape response: %s\n", str );
988            if( tr_bencDictFindInt( t, "nextScrapeTime", &i ) )
989                printf( "  Next scrape:     %s", getTrackerDateStr( (time_t)i, isStopped ) );
990            if( tr_bencDictFindInt( t, "seeders", &i ) && tr_bencDictFindInt( t, "leechers", &j ) )
991                printf( "  Tracker knows of %" PRId64 " seeders and %" PRId64 " leechers\n", i, j );
992            if( tr_bencDictFindInt( t, "timesCompleted", &i ) )
993                printf( "  Tracker has seen %" PRId64 " clients complete this torrent\n", i );
994            printf( "\n" );
995
996            printf( "ORIGINS\n" );
997            if( tr_bencDictFindInt( t, "dateCreated", &i ) && i )
998            {
999                const time_t tt = i;
1000                printf( "  Date created: %s", ctime( &tt ) );
1001            }
1002            if( tr_bencDictFindInt( t, "isPrivate", &i ) )
1003                printf( "  Public torrent: %s\n", ( i ? "No" : "Yes" ) );
1004            if( tr_bencDictFindStr( t, "comment", &str ) && str && *str )
1005                printf( "  Comment: %s\n", str );
1006            if( tr_bencDictFindStr( t, "creator", &str ) && str && *str )
1007                printf( "  Creator: %s\n", str );
1008            if( tr_bencDictFindInt( t, "pieceCount", &i ) )
1009                printf( "  Piece Count: %" PRId64 "\n", i );
1010            if( tr_bencDictFindInt( t, "pieceSize", &i ) )
1011                printf( "  Piece Size: %" PRId64 "\n", i );
1012            printf( "\n" );
1013
1014            printf("PIECES\n  ");
1015            if( tr_bencDictFindRaw( t, "pieces", &raw, &rawlen ) && tr_bencDictFindInt( t, "pieceCount", &j ) ) {
1016                int len;
1017                char * str = tr_base64_decode( raw, rawlen, &len );
1018                for( i=k=0; k<len; ++k ) {
1019                    int e;
1020                    for( e=0; i<j && e<8; ++e, ++i )
1021                        printf( str[k] & (1<<(7-e)) ? "1" : "0" );
1022                    printf( " " );
1023                    if( !(i%64) )
1024                        printf( "\n  " );
1025                }
1026                tr_free( str );
1027            }
1028            printf( "\n" );
1029        }
1030    }
1031}
1032
1033static void
1034printFileList( tr_benc * top )
1035{
1036    tr_benc *args, *torrents;
1037
1038    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
1039      && ( tr_bencDictFindList( args, "torrents", &torrents ) ) )
1040    {
1041        int i, in;
1042        for( i = 0, in = tr_bencListSize( torrents ); i < in; ++i )
1043        {
1044            tr_benc *    d = tr_bencListChild( torrents, i );
1045            tr_benc *    files, *priorities, *wanteds;
1046            const char * name;
1047            if( tr_bencDictFindStr( d, "name", &name )
1048              && tr_bencDictFindList( d, "files", &files )
1049              && tr_bencDictFindList( d, "priorities", &priorities )
1050              && tr_bencDictFindList( d, "wanted", &wanteds ) )
1051            {
1052                int j = 0, jn = tr_bencListSize( files );
1053                printf( "%s (%d files):\n", name, jn );
1054                printf( "%3s  %4s %8s %3s %9s  %s\n", "#", "Done",
1055                        "Priority", "Get", "Size",
1056                        "Name" );
1057                for( j = 0, jn = tr_bencListSize( files ); j < jn; ++j )
1058                {
1059                    int64_t      have;
1060                    int64_t      length;
1061                    int64_t      priority;
1062                    int64_t      wanted;
1063                    const char * filename;
1064                    tr_benc *    file = tr_bencListChild( files, j );
1065                    if( tr_bencDictFindInt( file, "length", &length )
1066                      && tr_bencDictFindStr( file, "name", &filename )
1067                      && tr_bencDictFindInt( file, "bytesCompleted", &have )
1068                      && tr_bencGetInt( tr_bencListChild( priorities,
1069                                                          j ), &priority )
1070                      && tr_bencGetInt( tr_bencListChild( wanteds,
1071                                                          j ), &wanted ) )
1072                    {
1073                        char         sizestr[64];
1074                        double       percent = (double)have / length;
1075                        const char * pristr;
1076                        strlsize( sizestr, length, sizeof( sizestr ) );
1077                        switch( priority )
1078                        {
1079                            case TR_PRI_LOW:
1080                                pristr = "Low"; break;
1081
1082                            case TR_PRI_HIGH:
1083                                pristr = "High"; break;
1084
1085                            default:
1086                                pristr = "Normal"; break;
1087                        }
1088                        printf( "%3d: %3.0f%% %-8s %-3s %9s  %s\n",
1089                                j,
1090                                ( 100.0 * percent ),
1091                                pristr,
1092                                ( wanted ? "Yes" : "No" ),
1093                                sizestr,
1094                                filename );
1095                    }
1096                }
1097            }
1098        }
1099    }
1100}
1101
1102static void
1103printPeersImpl( tr_benc * peers )
1104{
1105    int i, n;
1106    printf( "%-20s  %-12s  %-5s %-6s  %-6s  %s\n",
1107            "Address", "Flags", "Done", "Down", "Up", "Client" );
1108    for( i = 0, n = tr_bencListSize( peers ); i < n; ++i )
1109    {
1110        double progress;
1111        const char * address, * client, * flagstr;
1112        int64_t rateToClient, rateToPeer;
1113        tr_benc * d = tr_bencListChild( peers, i );
1114
1115        if( tr_bencDictFindStr( d, "address", &address )
1116          && tr_bencDictFindStr( d, "clientName", &client )
1117          && tr_bencDictFindDouble( d, "progress", &progress )
1118          && tr_bencDictFindStr( d, "flagStr", &flagstr )
1119          && tr_bencDictFindInt( d, "rateToClient", &rateToClient )
1120          && tr_bencDictFindInt( d, "rateToPeer", &rateToPeer ) )
1121        {
1122            printf( "%-20s  %-12s  %-5.1f %6.1f  %6.1f  %s\n",
1123                    address, flagstr, (progress*100.0),
1124                    rateToClient / 1024.0,
1125                    rateToPeer / 1024.0,
1126                    client );
1127        }
1128    }
1129}
1130
1131static void
1132printPeers( tr_benc * top )
1133{
1134    tr_benc *args, *torrents;
1135
1136    if( tr_bencDictFindDict( top, "arguments", &args )
1137      && tr_bencDictFindList( args, "torrents", &torrents ) )
1138    {
1139        int i, n;
1140        for( i=0, n=tr_bencListSize( torrents ); i<n; ++i )
1141        {
1142            tr_benc * peers;
1143            tr_benc * torrent = tr_bencListChild( torrents, i );
1144            if( tr_bencDictFindList( torrent, "peers", &peers ) ) {
1145                printPeersImpl( peers );
1146                if( i+1<n )
1147                    printf( "\n" );
1148            }
1149        }
1150    }
1151}
1152
1153static void
1154printTorrentList( tr_benc * top )
1155{
1156    tr_benc *args, *list;
1157
1158    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
1159      && ( tr_bencDictFindList( args, "torrents", &list ) ) )
1160    {
1161        int i, n;
1162        int64_t total_up = 0, total_down = 0, total_size = 0;
1163        char haveStr[32];
1164
1165        printf( "%-4s   %-4s  %9s  %-8s  %6s  %6s  %-5s  %-11s  %s\n",
1166                "ID", "Done", "Have", "ETA", "Up", "Down", "Ratio", "Status",
1167                "Name" );
1168
1169        for( i = 0, n = tr_bencListSize( list ); i < n; ++i )
1170        {
1171            int64_t      id, eta, status, up, down;
1172            int64_t      sizeWhenDone, leftUntilDone;
1173            double       ratio;
1174            const char * name;
1175            tr_benc *   d = tr_bencListChild( list, i );
1176            if( tr_bencDictFindInt( d, "eta", &eta )
1177              && tr_bencDictFindInt( d, "id", &id )
1178              && tr_bencDictFindInt( d, "leftUntilDone", &leftUntilDone )
1179              && tr_bencDictFindStr( d, "name", &name )
1180              && tr_bencDictFindInt( d, "rateDownload", &down )
1181              && tr_bencDictFindInt( d, "rateUpload", &up )
1182              && tr_bencDictFindInt( d, "sizeWhenDone", &sizeWhenDone )
1183              && tr_bencDictFindInt( d, "status", &status )
1184              && tr_bencDictFindDouble( d, "uploadRatio", &ratio ) )
1185            {
1186                char etaStr[16];
1187                char statusStr[64];
1188                char ratioStr[32];
1189                char doneStr[8];
1190                int64_t error;
1191                char errorMark;
1192
1193                if( sizeWhenDone )
1194                    tr_snprintf( doneStr, sizeof( doneStr ), "%d%%", (int)( 100.0 * ( sizeWhenDone - leftUntilDone ) / sizeWhenDone ) );
1195                else
1196                    tr_strlcpy( doneStr, "n/a", sizeof( doneStr ) );
1197
1198                strlsize( haveStr, sizeWhenDone - leftUntilDone, sizeof( haveStr ) );
1199
1200                if( leftUntilDone )
1201                    etaToString( etaStr, sizeof( etaStr ), eta );
1202                else
1203                    tr_snprintf( etaStr, sizeof( etaStr ), "Done" );
1204                if( tr_bencDictFindInt( d, "error", &error ) && error )
1205                    errorMark = '*';
1206                else
1207                    errorMark = ' ';
1208                printf(
1209                    "%4d%c  %4s  %9s  %-8s  %6.1f  %6.1f  %5s  %-11s  %s\n",
1210                    (int)id, errorMark,
1211                    doneStr,
1212                    haveStr,
1213                    etaStr,
1214                    up / 1024.0,
1215                    down / 1024.0,
1216                    strlratio2( ratioStr, ratio, sizeof( ratioStr ) ),
1217                    getStatusString( d, statusStr, sizeof( statusStr ) ),
1218                    name );
1219
1220                total_up += up;
1221                total_down += down;
1222                total_size += sizeWhenDone - leftUntilDone;
1223            }
1224        }
1225
1226        printf( "Sum:         %9s            %6.1f  %6.1f\n",
1227                strlsize( haveStr, total_size, sizeof( haveStr ) ),
1228                total_up / 1024.0,
1229                total_down / 1024.0 );
1230    }
1231}
1232
1233static void
1234processResponse( const char * host,
1235                 int          port,
1236                 const void * response,
1237                 size_t       len )
1238{
1239    tr_benc top;
1240
1241    if( debug )
1242        fprintf( stderr, "got response:\n--------\n%*.*s\n--------\n",
1243                 (int)len, (int)len, (const char*) response );
1244
1245    if( tr_jsonParse( response, len, &top, NULL ) )
1246        tr_nerr( MY_NAME, "Unable to parse response \"%*.*s\"", (int)len,
1247                 (int)len, (char*)response );
1248    else
1249    {
1250        int64_t      tag = -1;
1251        const char * str;
1252        tr_bencDictFindInt( &top, "tag", &tag );
1253
1254        switch( tag )
1255        {
1256            case TAG_SESSION:
1257                printSession( &top ); break;
1258
1259            case TAG_FILES:
1260                printFileList( &top ); break;
1261
1262            case TAG_DETAILS:
1263                printDetails( &top ); break;
1264
1265            case TAG_LIST:
1266                printTorrentList( &top ); break;
1267
1268            case TAG_PEERS:
1269                printPeers( &top ); break;
1270
1271            default:
1272                if( tr_bencDictFindStr( &top, "result", &str ) )
1273                    printf( "%s:%d responded: \"%s\"\n", host, port, str );
1274        }
1275
1276        tr_bencFree( &top );
1277    }
1278}
1279
1280static void
1281processRequests( const char *  host,
1282                 int           port,
1283                 const char ** reqs,
1284                 int           reqCount )
1285{
1286    int               i;
1287    CURL *            curl;
1288    struct evbuffer * buf = evbuffer_new( );
1289    char *            url = tr_strdup_printf(
1290        "http://%s:%d/transmission/rpc", host, port );
1291
1292    curl = curl_easy_init( );
1293    curl_easy_setopt( curl, CURLOPT_VERBOSE, debug );
1294#ifdef HAVE_LIBZ
1295    curl_easy_setopt( curl, CURLOPT_ENCODING, "deflate" );
1296#endif
1297    curl_easy_setopt( curl, CURLOPT_USERAGENT, MY_NAME "/" LONG_VERSION_STRING );
1298    curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, writeFunc );
1299    curl_easy_setopt( curl, CURLOPT_WRITEDATA, buf );
1300    curl_easy_setopt( curl, CURLOPT_POST, 1 );
1301    curl_easy_setopt( curl, CURLOPT_URL, url );
1302    curl_easy_setopt( curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL );
1303    curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
1304    if( netrc )
1305        curl_easy_setopt( curl, CURLOPT_NETRC_FILE, netrc );
1306    if( auth )
1307        curl_easy_setopt( curl, CURLOPT_USERPWD, auth );
1308
1309    for( i = 0; i < reqCount; ++i )
1310    {
1311        CURLcode res;
1312        curl_easy_setopt( curl, CURLOPT_POSTFIELDS, reqs[i] );
1313        if( debug )
1314            fprintf( stderr, "posting:\n--------\n%s\n--------\n", reqs[i] );
1315        if( ( res = curl_easy_perform( curl ) ) )
1316            tr_nerr( MY_NAME, "(%s:%d) %s", host, port,
1317                    curl_easy_strerror( res ) );
1318        else
1319            processResponse( host, port, EVBUFFER_DATA(
1320                                buf ), EVBUFFER_LENGTH( buf ) );
1321
1322        evbuffer_drain( buf, EVBUFFER_LENGTH( buf ) );
1323    }
1324
1325    /* cleanup */
1326    tr_free( url );
1327    evbuffer_free( buf );
1328    curl_easy_cleanup( curl );
1329}
1330
1331int
1332main( int     argc,
1333      char ** argv )
1334{
1335    int    i;
1336    int    port = DEFAULT_PORT;
1337    char * host = NULL;
1338
1339    if( argc < 2 )
1340        showUsage( );
1341
1342    getHostAndPort( &argc, argv, &host, &port );
1343    if( host == NULL )
1344        host = tr_strdup( DEFAULT_HOST );
1345
1346    readargs( argc, (const char**)argv );
1347    if( reqCount )
1348        processRequests( host, port, (const char**)reqs, reqCount );
1349    else
1350        showUsage( );
1351
1352    for( i = 0; i < reqCount; ++i )
1353        tr_free( reqs[i] );
1354
1355    tr_free( host );
1356    return 0;
1357}
1358
Note: See TracBrowser for help on using the repository browser.