source: branches/1.5x/daemon/remote.c @ 8275

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

(1.5x daemon) fix rounding error in transmission-remote display. Patch by wereHamster

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