source: trunk/daemon/remote.c @ 7163

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

(daemon) #1520: "transmission-remote -l" doesn't show infinite ratio properly

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