source: trunk/daemon/remote.c @ 8016

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

(trunk daemon) #1881: Displayed ratio should be truncated, not rounded

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