source: trunk/daemon/remote.c @ 7787

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

(trunk) same as r7786, but in daemon & gtk too

  • Property svn:keywords set to Date Rev Author Id
File size: 41.1 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 7787 2009-01-24 00:28:41Z 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,
634          int64_t size,
635          size_t  buflen )
636{
637    if( !size )
638        tr_strlcpy( buf, "None", buflen );
639    else if( size < (int64_t)KILOBYTE_FACTOR )
640        tr_snprintf( buf, buflen, "%'" PRId64 " bytes", (int64_t)size );
641    else
642    {
643        double displayed_size;
644        if( size < (int64_t)MEGABYTE_FACTOR )
645        {
646            displayed_size = (double) size / KILOBYTE_FACTOR;
647            tr_snprintf( buf, buflen, "%'.1f KB", displayed_size );
648        }
649        else if( size < (int64_t)GIGABYTE_FACTOR )
650        {
651            displayed_size = (double) size / MEGABYTE_FACTOR;
652            tr_snprintf( buf, buflen, "%'.1f MB", displayed_size );
653        }
654        else
655        {
656            displayed_size = (double) size / GIGABYTE_FACTOR;
657            tr_snprintf( buf, buflen, "%'.1f GB", displayed_size );
658        }
659    }
660    return buf;
661}
662
663static char*
664getStatusString( tr_benc * t, char * buf, size_t buflen )
665{
666    int64_t status;
667
668    if( !tr_bencDictFindInt( t, "status", &status ) )
669    {
670        *buf = '\0';
671    }
672    else switch( status )
673    {
674        case TR_STATUS_STOPPED:
675            tr_strlcpy( buf, "Stopped", buflen );
676            break;
677
678        case TR_STATUS_CHECK_WAIT:
679        case TR_STATUS_CHECK: {
680            const char * str = status == TR_STATUS_CHECK_WAIT
681                             ? "Will Verify"
682                             : "Verifying";
683            double percent;
684            if( tr_bencDictFindDouble( t, "recheckProgress", &percent ) )
685                tr_snprintf( buf, buflen, "%s (%.0f%%)", str, percent*100.0 );
686            else
687                tr_strlcpy( buf, str, buflen );
688
689            break;
690        }
691
692        case TR_STATUS_DOWNLOAD:
693        case TR_STATUS_SEED: {
694            int64_t fromUs = 0;
695            int64_t toUs = 0;
696            tr_bencDictFindInt( t, "peersGettingFromUs", &fromUs );
697            tr_bencDictFindInt( t, "peersSendingToUs", &toUs );
698            if( fromUs && toUs )
699                tr_strlcpy( buf, "Up & Down", buflen );
700            else if( toUs )
701                tr_strlcpy( buf, "Downloading", buflen );
702            else if( fromUs ) {
703                int64_t leftUntilDone = 0;
704                tr_bencDictFindInt( t, "leftUntilDone", &leftUntilDone );
705                if( leftUntilDone > 0 )
706                    tr_strlcpy( buf, "Uploading", buflen );
707                else
708                    tr_strlcpy( buf, "Seeding", buflen );
709            } else
710                tr_strlcpy( buf, "Idle", buflen );
711            break;
712        }
713    }
714
715    return buf;
716}
717
718static const char*
719getTrackerDateStr( const time_t t, tr_bool isStopped )
720{
721    const char * str;
722    switch( t ) {
723        case 0: str = isStopped ? "None (Stopped)\n" : "None\n"; break;
724        case 1: str = "In Progress\n"; break;
725        default: str = ctime( &t ); break;
726    }
727    return str;
728}
729
730static void
731printSession( tr_benc * top )
732{
733    tr_benc *args;
734    if( ( tr_bencDictFindDict( top, "arguments", &args ) ) )
735    {
736        const char * str;
737        int64_t      i;
738
739        printf( "VERSION\n" );
740        if( tr_bencDictFindStr( args,  "version", &str ) )
741            printf( "  Daemon version: %s\n", str );
742        if( tr_bencDictFindInt( args, "rpc-version", &i ) )
743            printf( "  RPC version: %" PRId64 "\n", i );
744        if( tr_bencDictFindInt( args, "rpc-version-minimum", &i ) )
745            printf( "  RPC minimum version: %" PRId64 "\n", i );
746        printf( "\n" );
747
748        printf( "TRANSFER\n" );
749        if( tr_bencDictFindStr( args,  "download-dir", &str ) )
750            printf( "  Download directory: %s\n", str );
751        if( tr_bencDictFindInt( args, "port", &i ) )
752            printf( "  Listenport: %" PRId64 "\n", i );
753        if( tr_bencDictFindInt( args, "port-forwarding-enabled", &i ) )
754            printf( "  Portforwarding enabled: %s\n", ( i ? "Yes" : "No" ) );
755        if( tr_bencDictFindInt( args, "pex-allowed", &i ) )
756            printf( "  Peer exchange allowed: %s\n", ( i ? "Yes" : "No" ) );
757        if( tr_bencDictFindStr( args,  "encryption", &str ) )
758            printf( "  Encryption: %s\n", str );
759        printf( "\n" );
760
761        printf( "LIMITS\n" );
762        if( tr_bencDictFindInt( args, "peer-limit", &i ) )
763            printf( "  Peer limit: %" PRId64 "\n", i );
764        if( tr_bencDictFindInt( args, "speed-limit-down-enabled", &i ) )
765            printf( "  Downloadlimit enabled: %s\n", ( i ? "Yes" : "No" ) );
766        if( tr_bencDictFindInt( args, "speed-limit-down", &i ) )
767            printf( "  Downloadlimit: %6" PRId64 " KB/sec\n", i );
768        if( tr_bencDictFindInt( args, "speed-limit-up-enabled", &i ) )
769            printf( "  Uploadlimit enabled:   %s\n", ( i ? "Yes" : "No" ) );
770        if( tr_bencDictFindInt( args, "speed-limit-up", &i ) )
771            printf( "  Uploadlimit:   %6" PRId64 " KB/sec\n", i );
772               
773    }
774}
775
776static void
777printDetails( tr_benc * top )
778{
779    tr_benc *args, *torrents;
780
781    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
782      && ( tr_bencDictFindList( args, "torrents", &torrents ) ) )
783    {
784        int ti, tCount;
785        for( ti = 0, tCount = tr_bencListSize( torrents ); ti < tCount;
786             ++ti )
787        {
788            tr_benc *    t = tr_bencListChild( torrents, ti );
789            tr_benc *    l;
790            const char * str;
791            char         buf[512];
792            char         buf2[512];
793            int64_t      i, j, k;
794            tr_bool      isStopped;
795
796            isStopped = tr_bencDictFindInt( t, "status", &i ) && (i==TR_STATUS_STOPPED);
797
798            printf( "NAME\n" );
799            if( tr_bencDictFindInt( t, "id", &i ) )
800                printf( "  Id: %" PRId64 "\n", i );
801            if( tr_bencDictFindStr( t, "name", &str ) )
802                printf( "  Name: %s\n", str );
803            if( tr_bencDictFindStr( t, "hashString", &str ) )
804                printf( "  Hash: %s\n", str );
805            printf( "\n" );
806
807            printf( "TRANSFER\n" );
808            getStatusString( t, buf, sizeof( buf ) );
809            printf( "  State: %s\n", buf );
810
811            if( tr_bencDictFindStr( t, "downloadDir", &str ) )
812                printf( "  Location: %s\n", str );
813
814            if( tr_bencDictFindInt( t, "sizeWhenDone", &i )
815              && tr_bencDictFindInt( t, "leftUntilDone", &j ) )
816            {
817                strlratio( buf, 100.0 * ( i - j ), i, sizeof( buf ) );
818                printf( "  Percent Done: %s%%\n", buf );
819            }
820
821            if( tr_bencDictFindInt( t, "eta", &i ) )
822            {
823                etaToString( buf, sizeof( buf ), i );
824                printf( "  ETA: %s\n", buf );
825            }
826            if( tr_bencDictFindInt( t, "rateDownload", &i ) )
827                printf( "  Download Speed: %.1f KB/s\n", i / 1024.0 );
828            if( tr_bencDictFindInt( t, "rateUpload", &i ) )
829                printf( "  Upload Speed: %.1f KB/s\n", i / 1024.0 );
830            if( tr_bencDictFindInt( t, "haveUnchecked", &i )
831              && tr_bencDictFindInt( t, "haveValid", &j ) )
832            {
833                strlsize( buf, i + j, sizeof( buf ) );
834                strlsize( buf2, j, sizeof( buf2 ) );
835                printf( "  Have: %s (%s verified)\n", buf, buf2 );
836            }
837
838            if( tr_bencDictFindInt( t, "sizeWhenDone", &i )
839              && tr_bencDictFindInt( t, "totalSize", &j ) )
840            {
841                strlsize( buf, j, sizeof( buf ) );
842                strlsize( buf2, i, sizeof( buf2 ) );
843                printf( "  Total size: %s (%s wanted)\n", buf, buf2 );
844            }
845            if( tr_bencDictFindInt( t, "downloadedEver", &i )
846              && tr_bencDictFindInt( t, "uploadedEver", &j ) )
847            {
848                strlsize( buf, i, sizeof( buf ) );
849                printf( "  Downloaded: %s\n", buf );
850                strlsize( buf, j, sizeof( buf ) );
851                printf( "  Uploaded: %s\n", buf );
852                strlratio( buf, j, i, sizeof( buf ) );
853                printf( "  Ratio: %s\n", buf );
854            }
855            if( tr_bencDictFindInt( t, "corruptEver", &i ) )
856            {
857                strlsize( buf, i, sizeof( buf ) );
858                printf( "  Corrupt DL: %s\n", buf );
859            }
860            if( tr_bencDictFindStr( t, "errorString", &str ) && str && *str )
861                printf( "  Error: %s\n", str );
862
863            if( tr_bencDictFindInt( t, "peersConnected", &i )
864              && tr_bencDictFindInt( t, "peersGettingFromUs", &j )
865              && tr_bencDictFindInt( t, "peersSendingToUs", &k ) )
866            {
867                printf(
868                    "  Peers: "
869                    "connected to %" PRId64 ", "
870                                            "uploading to %" PRId64
871                    ", "
872                    "downloading from %"
873                    PRId64 "\n",
874                    i, j, k );
875            }
876
877            if( tr_bencDictFindList( t, "webseeds", &l )
878              && tr_bencDictFindInt( t, "webseedsSendingToUs", &i ) )
879            {
880                const int64_t n = tr_bencListSize( l );
881                if( n > 0 )
882                    printf(
883                        "  Web Seeds: downloading from %" PRId64 " of %"
884                        PRId64
885                        " web seeds\n", i, n );
886            }
887            printf( "\n" );
888
889            printf( "HISTORY\n" );
890            if( tr_bencDictFindInt( t, "addedDate", &i ) && i )
891            {
892                const time_t tt = i;
893                printf( "  Date added:      %s", ctime( &tt ) );
894            }
895            if( tr_bencDictFindInt( t, "doneDate", &i ) && i )
896            {
897                const time_t tt = i;
898                printf( "  Date finished:   %s", ctime( &tt ) );
899            }
900            if( tr_bencDictFindInt( t, "startDate", &i ) && i )
901            {
902                const time_t tt = i;
903                printf( "  Date started:    %s", ctime( &tt ) );
904            }
905            if( tr_bencDictFindInt( t, "activityDate", &i ) && i )
906            {
907                const time_t tt = i;
908                printf( "  Latest activity: %s", ctime( &tt ) );
909            }
910            printf( "\n" );
911
912            printf( "TRACKER\n" );
913            if( tr_bencDictFindInt( t, "lastAnnounceTime", &i ) )
914                printf( "  Latest announce: %s", getTrackerDateStr( (time_t)i, isStopped ) );
915            if( tr_bencDictFindStr( t, "announceURL", &str ) )
916                printf( "  Announce URL: %s\n", str );
917            if( tr_bencDictFindStr( t, "announceResponse", &str ) && str && *str )
918                printf( "  Announce response: %s\n", str );
919            if( tr_bencDictFindInt( t, "nextAnnounceTime", &i ) )
920                printf( "  Next announce:   %s", getTrackerDateStr( (time_t)i, isStopped ) );
921            if( tr_bencDictFindInt( t, "lastScrapeTime", &i ) )
922                printf( "  Latest scrape:   %s", getTrackerDateStr( (time_t)i, isStopped ) );
923            if( tr_bencDictFindStr( t, "scrapeResponse", &str ) )
924                printf( "  Scrape response: %s\n", str );
925            if( tr_bencDictFindInt( t, "nextScrapeTime", &i ) )
926                printf( "  Next scrape:     %s", getTrackerDateStr( (time_t)i, isStopped ) );
927            if( tr_bencDictFindInt( t, "seeders", &i ) && tr_bencDictFindInt( t, "leechers", &j ) )
928                printf( "  Tracker knows of %" PRId64 " seeders and %" PRId64 " leechers\n", i, j );
929            if( tr_bencDictFindInt( t, "timesCompleted", &i ) )
930                printf( "  Tracker has seen %" PRId64 " clients complete this torrent\n", i );
931            printf( "\n" );
932
933            printf( "ORIGINS\n" );
934            if( tr_bencDictFindInt( t, "dateCreated", &i ) && i )
935            {
936                const time_t tt = i;
937                printf( "  Date created: %s", ctime( &tt ) );
938            }
939            if( tr_bencDictFindInt( t, "isPrivate", &i ) )
940                printf( "  Public torrent: %s\n", ( i ? "No" : "Yes" ) );
941            if( tr_bencDictFindStr( t, "comment", &str ) && str && *str )
942                printf( "  Comment: %s\n", str );
943            if( tr_bencDictFindStr( t, "creator", &str ) && str && *str )
944                printf( "  Creator: %s\n", str );
945            if( tr_bencDictFindInt( t, "pieceCount", &i ) )
946                printf( "  Piece Count: %" PRId64 "\n", i );
947            if( tr_bencDictFindInt( t, "pieceSize", &i ) )
948                printf( "  Piece Size: %" PRId64 "\n", i );
949        }
950    }
951}
952
953static void
954printFileList( tr_benc * top )
955{
956    tr_benc *args, *torrents;
957
958    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
959      && ( tr_bencDictFindList( args, "torrents", &torrents ) ) )
960    {
961        int i, in;
962        for( i = 0, in = tr_bencListSize( torrents ); i < in; ++i )
963        {
964            tr_benc *    d = tr_bencListChild( torrents, i );
965            tr_benc *    files, *priorities, *wanteds;
966            const char * name;
967            if( tr_bencDictFindStr( d, "name", &name )
968              && tr_bencDictFindList( d, "files", &files )
969              && tr_bencDictFindList( d, "priorities", &priorities )
970              && tr_bencDictFindList( d, "wanted", &wanteds ) )
971            {
972                int j = 0, jn = tr_bencListSize( files );
973                printf( "%s (%d files):\n", name, jn );
974                printf( "%3s  %4s %8s %3s %9s  %s\n", "#", "Done",
975                        "Priority", "Get", "Size",
976                        "Name" );
977                for( j = 0, jn = tr_bencListSize( files ); j < jn; ++j )
978                {
979                    int64_t      have;
980                    int64_t      length;
981                    int64_t      priority;
982                    int64_t      wanted;
983                    const char * filename;
984                    tr_benc *    file = tr_bencListChild( files, j );
985                    if( tr_bencDictFindInt( file, "length", &length )
986                      && tr_bencDictFindStr( file, "name", &filename )
987                      && tr_bencDictFindInt( file, "bytesCompleted", &have )
988                      && tr_bencGetInt( tr_bencListChild( priorities,
989                                                          j ), &priority )
990                      && tr_bencGetInt( tr_bencListChild( wanteds,
991                                                          j ), &wanted ) )
992                    {
993                        char         sizestr[64];
994                        double       percent = (double)have / length;
995                        const char * pristr;
996                        strlsize( sizestr, length, sizeof( sizestr ) );
997                        switch( priority )
998                        {
999                            case TR_PRI_LOW:
1000                                pristr = "Low"; break;
1001
1002                            case TR_PRI_HIGH:
1003                                pristr = "High"; break;
1004
1005                            default:
1006                                pristr = "Normal"; break;
1007                        }
1008                        printf( "%3d: %3.0f%% %-8s %-3s %9s  %s\n",
1009                                j,
1010                                ( 100.0 * percent ),
1011                                pristr,
1012                                ( wanted ? "Yes" : "No" ),
1013                                sizestr,
1014                                filename );
1015                    }
1016                }
1017            }
1018        }
1019    }
1020}
1021
1022static void
1023printPeersImpl( tr_benc * peers )
1024{
1025    int i, n;
1026    printf( "%-20s  %-12s  %-5s %-6s  %-6s  %s\n",
1027            "Address", "Flags", "Done", "Down", "Up", "Client" );
1028    for( i = 0, n = tr_bencListSize( peers ); i < n; ++i )
1029    {
1030        double progress;
1031        const char * address, * client, * flagstr;
1032        int64_t rateToClient, rateToPeer;
1033        tr_benc * d = tr_bencListChild( peers, i );
1034
1035        if( tr_bencDictFindStr( d, "address", &address )
1036          && tr_bencDictFindStr( d, "clientName", &client )
1037          && tr_bencDictFindDouble( d, "progress", &progress )
1038          && tr_bencDictFindStr( d, "flagStr", &flagstr )
1039          && tr_bencDictFindInt( d, "rateToClient", &rateToClient )
1040          && tr_bencDictFindInt( d, "rateToPeer", &rateToPeer ) )
1041        {
1042            printf( "%-20s  %-12s  %-5.1f %6.1f  %6.1f  %s\n",
1043                    address, flagstr, (progress*100.0),
1044                    rateToClient / 1024.0,
1045                    rateToPeer / 1024.0,
1046                    client );
1047        }
1048    }
1049}
1050
1051static void
1052printPeers( tr_benc * top )
1053{
1054    tr_benc *args, *torrents;
1055
1056    if( tr_bencDictFindDict( top, "arguments", &args )
1057      && tr_bencDictFindList( args, "torrents", &torrents ) )
1058    {
1059        int i, n;
1060        for( i=0, n=tr_bencListSize( torrents ); i<n; ++i )
1061        {
1062            tr_benc * peers;
1063            tr_benc * torrent = tr_bencListChild( torrents, i );
1064            if( tr_bencDictFindList( torrent, "peers", &peers ) ) {
1065                printPeersImpl( peers );
1066                if( i+1<n )
1067                    printf( "\n" );
1068            }
1069        }
1070    }
1071}
1072
1073static void
1074printTorrentList( tr_benc * top )
1075{
1076    tr_benc *args, *list;
1077
1078    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
1079      && ( tr_bencDictFindList( args, "torrents", &list ) ) )
1080    {
1081        int i, n;
1082        printf( "%-4s  %-4s  %-8s  %-6s  %-6s  %-5s  %-11s  %s\n",
1083                "ID", "Done", "ETA", "Up", "Down", "Ratio", "Status",
1084                "Name" );
1085        for( i = 0, n = tr_bencListSize( list ); i < n; ++i )
1086        {
1087            int64_t      id, eta, status, up, down;
1088            int64_t      sizeWhenDone, leftUntilDone;
1089            double       ratio;
1090            const char * name;
1091            tr_benc *   d = tr_bencListChild( list, i );
1092            if( tr_bencDictFindInt( d, "eta", &eta )
1093              && tr_bencDictFindInt( d, "id", &id )
1094              && tr_bencDictFindInt( d, "leftUntilDone", &leftUntilDone )
1095              && tr_bencDictFindStr( d, "name", &name )
1096              && tr_bencDictFindInt( d, "rateDownload", &down )
1097              && tr_bencDictFindInt( d, "rateUpload", &up )
1098              && tr_bencDictFindInt( d, "sizeWhenDone", &sizeWhenDone )
1099              && tr_bencDictFindInt( d, "status", &status )
1100              && tr_bencDictFindDouble( d, "uploadRatio", &ratio ) )
1101            {
1102                char etaStr[16];
1103                char statusStr[64];
1104                char ratioStr[32];
1105
1106                if( leftUntilDone )
1107                    etaToString( etaStr, sizeof( etaStr ), eta );
1108                else
1109                    tr_snprintf( etaStr, sizeof( etaStr ), "Done" );
1110                printf(
1111                    "%4d  %3d%%  %-8s  %6.1f  %6.1f  %5s  %-11s  %s\n",
1112                    (int)id,
1113                    (int)( 100.0 *
1114                           ( sizeWhenDone - leftUntilDone ) / sizeWhenDone ),
1115                    etaStr,
1116                    up / 1024.0,
1117                    down / 1024.0,
1118                    strlratio2( ratioStr, ratio, sizeof( ratioStr ) ),
1119                    getStatusString( d, statusStr, sizeof( statusStr ) ),
1120                    name );
1121            }
1122        }
1123    }
1124}
1125
1126static void
1127processResponse( const char * host,
1128                 int          port,
1129                 const void * response,
1130                 size_t       len )
1131{
1132    tr_benc top;
1133
1134    if( debug )
1135        fprintf( stderr, "got response:\n--------\n%*.*s\n--------\n",
1136                 (int)len, (int)len, (const char*) response );
1137
1138    if( tr_jsonParse( response, len, &top, NULL ) )
1139        tr_nerr( MY_NAME, "Unable to parse response \"%*.*s\"", (int)len,
1140                 (int)len, (char*)response );
1141    else
1142    {
1143        int64_t      tag = -1;
1144        const char * str;
1145        tr_bencDictFindInt( &top, "tag", &tag );
1146
1147        switch( tag )
1148        {
1149            case TAG_SESSION:
1150                printSession( &top ); break;
1151
1152            case TAG_FILES:
1153                printFileList( &top ); break;
1154
1155            case TAG_DETAILS:
1156                printDetails( &top ); break;
1157
1158            case TAG_LIST:
1159                printTorrentList( &top ); break;
1160
1161            case TAG_PEERS:
1162                printPeers( &top ); break;
1163
1164            default:
1165                if( tr_bencDictFindStr( &top, "result", &str ) )
1166                    printf( "%s:%d responded: \"%s\"\n", host, port, str );
1167        }
1168
1169        tr_bencFree( &top );
1170    }
1171}
1172
1173static void
1174processRequests( const char *  host,
1175                 int           port,
1176                 const char ** reqs,
1177                 int           reqCount )
1178{
1179    int               i;
1180    CURL *            curl;
1181    struct evbuffer * buf = evbuffer_new( );
1182    char *            url = tr_strdup_printf(
1183        "http://%s:%d/transmission/rpc", host, port );
1184
1185    curl = curl_easy_init( );
1186    curl_easy_setopt( curl, CURLOPT_VERBOSE, debug );
1187#ifdef HAVE_LIBZ
1188    curl_easy_setopt( curl, CURLOPT_ENCODING, "deflate" );
1189#endif
1190    curl_easy_setopt( curl, CURLOPT_USERAGENT,
1191                      MY_NAME "/" LONG_VERSION_STRING );
1192    curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, writeFunc );
1193    curl_easy_setopt( curl, CURLOPT_WRITEDATA, buf );
1194    curl_easy_setopt( curl, CURLOPT_POST, 1 );
1195    curl_easy_setopt( curl, CURLOPT_URL, url );
1196    if( auth )
1197    {
1198        curl_easy_setopt( curl, CURLOPT_USERPWD, auth );
1199        curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
1200    }
1201
1202    for( i = 0; i < reqCount; ++i )
1203    {
1204        CURLcode res;
1205        curl_easy_setopt( curl, CURLOPT_POSTFIELDS, reqs[i] );
1206        if( debug )
1207            fprintf( stderr, "posting:\n--------\n%s\n--------\n", reqs[i] );
1208        if( ( res = curl_easy_perform( curl ) ) )
1209            tr_nerr( MY_NAME, "(%s:%d) %s", host, port,
1210                    curl_easy_strerror( res ) );
1211        else
1212            processResponse( host, port, EVBUFFER_DATA(
1213                                buf ), EVBUFFER_LENGTH( buf ) );
1214
1215        evbuffer_drain( buf, EVBUFFER_LENGTH( buf ) );
1216    }
1217
1218    /* cleanup */
1219    tr_free( url );
1220    evbuffer_free( buf );
1221    curl_easy_cleanup( curl );
1222}
1223
1224int
1225main( int     argc,
1226      char ** argv )
1227{
1228    int    i;
1229    int    port = DEFAULT_PORT;
1230    char * host = NULL;
1231
1232    if( argc < 2 )
1233        showUsage( );
1234
1235    getHostAndPort( &argc, argv, &host, &port );
1236    if( host == NULL )
1237        host = tr_strdup( DEFAULT_HOST );
1238
1239    readargs( argc, (const char**)argv );
1240    if( reqCount )
1241        processRequests( host, port, (const char**)reqs, reqCount );
1242    else
1243        showUsage( );
1244
1245    for( i = 0; i < reqCount; ++i )
1246        tr_free( reqs[i] );
1247
1248    tr_free( host );
1249    return 0;
1250}
1251
Note: See TracBrowser for help on using the repository browser.