source: trunk/daemon/remote.c @ 7903

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

(trunk daemon) #1843: status not shown anymore

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