source: trunk/daemon/remote.c @ 7835

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

(trunk libT) #1777: Size column in transmission-remote

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