source: trunk/daemon/remote.c @ 7202

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