source: trunk/daemon/remote.c @ 7785

Last change on this file since 7785 was 7785, checked in by charles, 14 years ago

(trunk libT) #1723: View version with / from transmission-remote

  • Property svn:keywords set to Date Rev Author Id
File size: 41.0 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 7785 2009-01-23 19:36:34Z charles $
11 */
12
13#include <errno.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h> /* strcmp */
17
18#ifdef WIN32
19 #include <direct.h> /* getcwd */
20#else
21 #include <unistd.h> /* getcwd */
22#endif
23
24#include <libevent/event.h>
25#include <curl/curl.h>
26
27#include <libtransmission/transmission.h>
28#include <libtransmission/bencode.h>
29#include <libtransmission/rpcimpl.h>
30#include <libtransmission/json.h>
31#include <libtransmission/tr-getopt.h>
32#include <libtransmission/utils.h>
33#include <libtransmission/version.h>
34
35#define MY_NAME "transmission-remote"
36#define DEFAULT_HOST "localhost"
37#define DEFAULT_PORT atoi(TR_DEFAULT_RPC_PORT_STR)
38
39enum { TAG_SESSION, TAG_LIST, TAG_DETAILS, TAG_FILES, TAG_PEERS };
40
41static const char*
42getUsage( void )
43{
44    return
45        "Transmission " LONG_VERSION_STRING
46        "  http://www.transmissionbt.com/\n"
47        "A fast and easy BitTorrent client\n"
48        "\n"
49        "Usage: " MY_NAME
50        " [host] [options]\n"
51        "       "
52        MY_NAME " [port] [options]\n"
53                "       "
54        MY_NAME " [host:port] [options]\n"
55                "\n"
56                "See the man page for detailed explanations and many examples.";
57}
58
59static tr_option opts[] =
60{
61    { 'a', "add",                  "Add torrent files by filename or URL", "a",  0, NULL },
62    { 'b', "debug",                "Print debugging information", "b",  0, NULL },
63    { 'd', "downlimit",            "Set the maximum global download speed in KB/s", "d",  1, "<speed>" },
64    { 'D', "no-downlimit",         "Don't limit the global download speed", "D",  0, NULL },
65    { 910, "encryption-required",  "Encrypt all peer connections", "er", 0, NULL },
66    { 911, "encryption-preferred", "Prefer encrypted peer connections", "ep", 0, NULL },
67    { 912, "encryption-tolerated", "Prefer unencrypted peer connections", "et", 0, NULL },
68    { 'f', "files",                "List the current torrent's files", "f",  0, NULL },
69    { 'g', "get",                  "Mark files for download", "g",  1, "<files>" },
70    { 'G', "no-get",               "Mark files for not downloading", "G",  1, "<files>" },
71    { 'i', "info",                 "Show details of the current torrent(s)", "i",  0, NULL },
72    { 'l', "list",                 "List all torrents", "l",  0, NULL },
73    { 'm', "portmap",              "Enable portmapping via NAT-PMP or UPnP", "m",  0, NULL },
74    { 'M', "no-portmap",           "Disable portmapping", "M",  0, NULL },
75    { 'n', "auth",                 "Set authentication info", "n",  1, "<username:password>" },
76    { 'p', "port",                 "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "p", 1, "<port>" },
77    { 900, "priority-high",        "Set the files' priorities as high", "ph", 1, "<files>" },
78    { 901, "priority-normal",      "Set the files' priorities as normal", "pn", 1, "<files>" },
79    { 902, "priority-low",         "Set the files' priorities as low", "pl", 1, "<files>" },
80    { 'r', "remove",               "Remove the current torrent(s)", "r",  0, NULL },
81    { 'R', "remove-and-delete",    "Remove the current torrent(s) and delete local data", NULL, 0, NULL },
82    { 920, "session",              "Print session information", NULL,  0, NULL },
83    { 's', "start",                "Start the current torrent(s)", "s",  0, NULL },
84    { 'S', "stop",                 "Stop the current torrent(s)", "S",  0, NULL },
85    { 't', "torrent",              "Set the current torrent(s)", "t",  1, "<torrent>" },
86    { 'u', "uplimit",              "Set the maximum global upload speed in KB/s", "u",  1, "<speed>" },
87    { 'U', "no-uplimit",           "Don't limit the global upload speed", "U",  0, NULL },
88    { 'v', "verify",               "Verify the current torrent(s)", "v",  0, NULL },
89    { 'V', "version",              "Show version number and exit", "V", 0, NULL },
90    { 'w', "download-dir",         "Set the default download folder", "w",  1, "<path>" },
91    { 'x', "pex",                  "Enable peer exchange (PEX)", "x",  0, NULL },
92    { 'X', "no-pex",               "Disable peer exchange (PEX)", "X",  0, NULL },
93    { 'z', "peers",                "List the current torrent's peers", "z",  0, NULL },
94    {   0, NULL,                   NULL, NULL, 0, NULL }
95};
96
97static void
98showUsage( void )
99{
100    tr_getopt_usage( MY_NAME, getUsage( ), opts );
101    exit( 0 );
102}
103
104static int
105numarg( const char * arg )
106{
107    char *     end = NULL;
108    const long num = strtol( arg, &end, 10 );
109
110    if( *end )
111    {
112        fprintf( stderr, "Not a number: \"%s\"\n", arg );
113        showUsage( );
114    }
115    return num;
116}
117
118static char * reqs[256]; /* arbitrary max */
119static int    reqCount = 0;
120static int    debug = 0;
121static char * auth = NULL;
122
123static char* 
124tr_getcwd( void ) 
125{ 
126    char buf[2048]; 
127    *buf = '\0'; 
128#ifdef WIN32
129    _getcwd( buf, sizeof( buf ) ); 
130#else
131    getcwd( buf, sizeof( buf ) ); 
132#endif
133    return tr_strdup( buf ); 
134} 
135
136static char*
137absolutify( const char * path )
138{
139    char * buf;
140
141    if( *path == '/' )
142        buf = tr_strdup( path );
143    else {
144        char * cwd = tr_getcwd( );
145        buf = tr_buildPath( cwd, path, NULL );
146        tr_free( cwd );
147    }
148
149    return buf;
150}
151
152static char*
153getEncodedMetainfo( const char * filename )
154{
155    size_t    len = 0;
156    char *    b64 = NULL;
157    uint8_t * buf = tr_loadFile( filename, &len );
158
159    if( buf )
160    {
161        b64 = tr_base64_encode( buf, len, NULL );
162        tr_free( buf );
163    }
164    return b64;
165}
166
167static void
168addIdArg( tr_benc *    args,
169          const char * id )
170{
171    if( !*id )
172    {
173        fprintf(
174            stderr,
175            "No torrent specified!  Please use the -t option first.\n" );
176        id = "-1"; /* no torrent will have this ID, so should be a no-op */
177    }
178    if( strcmp( id, "all" ) )
179    {
180        tr_rpc_parse_list_str( tr_bencDictAdd( args,
181                                               "ids" ), id, strlen( id ) );
182    }
183}
184
185static void
186addFiles( tr_benc *    args,
187          const char * key,
188          const char * arg )
189{
190    tr_benc * files = tr_bencDictAddList( args, key, 100 );
191
192    if( !*arg )
193    {
194        fprintf( stderr, "No files specified!\n" );
195        arg = "-1"; /* no file will have this index, so should be a no-op */
196    }
197    if( strcmp( arg, "all" ) )
198    {
199        tr_rpc_parse_list_str( files, arg, strlen( arg ) );
200    }
201}
202
203#define TR_N_ELEMENTS( ary ) ( sizeof( ary ) / sizeof( *ary ) )
204
205static const char * files_keys[] = {
206    "files",
207    "name",
208    "priorities",
209    "wanted"
210};
211
212static const char * details_keys[] = {
213    "activityDate",
214    "addedDate",
215    "announceResponse",
216    "announceURL",
217    "comment",
218    "corruptEver",
219    "creator",
220    "dateCreated",
221    "doneDate",
222    "downloadDir",
223    "downloadedEver",
224    "errorString",
225    "eta",
226    "hashString",
227    "haveUnchecked",
228    "haveValid",
229    "id",
230    "isPrivate",
231    "lastAnnounceTime",
232    "lastScrapeTime",
233    "leechers",
234    "leftUntilDone",
235    "name",
236    "nextAnnounceTime",
237    "nextScrapeTime",
238    "peersConnected",
239    "peersGettingFromUs",
240    "peersSendingToUs",
241    "pieceCount",
242    "pieceSize",
243    "rateDownload",
244    "rateUpload",
245    "recheckProgress",
246    "scrapeResponse",
247    "seeders",
248    "sizeWhenDone",
249    "startDate",
250    "status",
251    "timesCompleted",
252    "totalSize",
253    "uploadedEver",
254    "webseeds",
255    "webseedsSendingToUs"
256};
257
258static const char * list_keys[] = {
259    "eta",
260    "id",
261    "leftUntilDone",
262    "name",
263    "peersGettingFromUs",
264    "peersSendingToUs",
265    "rateDownload",
266    "rateUpload",
267    "sizeWhenDone",
268    "status",
269    "uploadRatio"
270};
271
272static void
273readargs( int           argc,
274          const char ** argv )
275{
276    int          c;
277    int          addingTorrents = 0;
278    const char * optarg;
279    char         id[4096];
280
281    *id = '\0';
282
283    while( ( c = tr_getopt( getUsage( ), argc, argv, opts, &optarg ) ) )
284    {
285        int     i, n;
286        int     addArg = TRUE;
287        tr_benc top, *args, *fields;
288        tr_bencInitDict( &top, 3 );
289        args = tr_bencDictAddDict( &top, "arguments", 0 );
290
291        switch( c )
292        {
293            case TR_OPT_UNK:
294                if( addingTorrents )
295                {
296                    char * tmp = getEncodedMetainfo( optarg );
297                    if( tmp )
298                    {
299                        tr_bencDictAddStr( &top, "method", "torrent-add" );
300                        tr_bencDictAddStr( args, "metainfo", tmp );
301                        tr_free( tmp );
302                    }
303                    else
304                    {
305                        tr_bencDictAddStr( &top, "method", "torrent-add" );
306                        tr_bencDictAddStr( args, "filename", optarg );
307                    }
308                }
309                else
310                {
311                    fprintf( stderr, "Unknown option: %s\n", optarg );
312                    addArg = FALSE;
313                }
314                break;
315
316            case 'a':
317                addingTorrents = 1;
318                addArg = FALSE;
319                break;
320
321            case 'b':
322                debug = 1;
323                addArg = FALSE;
324                break;
325
326            case 'd':
327                tr_bencDictAddStr( &top, "method", "session-set" );
328                tr_bencDictAddInt( args, "speed-limit-down", numarg( optarg ) );
329                tr_bencDictAddInt( args, "speed-limit-down-enabled", 1 );
330                break;
331
332            case 'D':
333                tr_bencDictAddStr( &top, "method", "session-set" );
334                tr_bencDictAddInt( args, "speed-limit-down-enabled", 0 );
335                break;
336
337            case 'f':
338                tr_bencDictAddStr( &top, "method", "torrent-get" );
339                tr_bencDictAddInt( &top, "tag", TAG_FILES );
340                addIdArg( args, id );
341                n = TR_N_ELEMENTS( files_keys );
342                fields = tr_bencDictAddList( args, "fields", n );
343                for( i = 0; i < n; ++i )
344                    tr_bencListAddStr( fields, files_keys[i] );
345                break;
346
347            case 'g':
348                tr_bencDictAddStr( &top, "method", "torrent-set" );
349                addIdArg( args, id );
350                addFiles( args, "files-wanted", optarg );
351                break;
352
353            case 'G':
354                tr_bencDictAddStr( &top, "method", "torrent-set" );
355                addIdArg( args, id );
356                addFiles( args, "files-unwanted", optarg );
357                break;
358
359            case 'i':
360                tr_bencDictAddStr( &top, "method", "torrent-get" );
361                tr_bencDictAddInt( &top, "tag", TAG_DETAILS );
362                addIdArg( args, id );
363                n = TR_N_ELEMENTS( details_keys );
364                fields = tr_bencDictAddList( args, "fields", n );
365                for( i = 0; i < n; ++i )
366                    tr_bencListAddStr( fields, details_keys[i] );
367                break;
368
369            case 'l':
370                tr_bencDictAddStr( &top, "method", "torrent-get" );
371                tr_bencDictAddInt( &top, "tag", TAG_LIST );
372                n = TR_N_ELEMENTS( list_keys );
373                fields = tr_bencDictAddList( args, "fields", n );
374                for( i = 0; i < n; ++i )
375                    tr_bencListAddStr( fields, list_keys[i] );
376                break;
377
378            case 'm':
379                tr_bencDictAddStr( &top, "method", "session-set" );
380                tr_bencDictAddInt( args, "port-forwarding-enabled", 1 );
381                break;
382
383            case 'M':
384                tr_bencDictAddStr( &top, "method", "session-set" );
385                tr_bencDictAddInt( args, "port-forwarding-enabled", 0 );
386                break;
387
388            case 'n':
389                auth = tr_strdup( optarg );
390                addArg = FALSE;
391                break;
392
393            case 'p':
394                tr_bencDictAddStr( &top, "method", "session-set" );
395                tr_bencDictAddInt( args, "port", numarg( optarg ) );
396                break;
397
398            case 'r':
399                tr_bencDictAddStr( &top, "method", "torrent-remove" );
400                addIdArg( args, id );
401                break;
402
403            case 'R':
404                tr_bencDictAddStr( &top, "method", "torrent-remove" );
405                addIdArg( args, id );
406                tr_bencDictAddInt( args, "delete-local-data", 1 );
407                break;
408
409            case 's':
410                tr_bencDictAddStr( &top, "method", "torrent-start" );
411                addIdArg( args, id );
412                break;
413
414            case 'S':
415                tr_bencDictAddStr( &top, "method", "torrent-stop" );
416                addIdArg( args, id );
417                break;
418
419            case 't':
420                tr_strlcpy( id, optarg, sizeof( id ) );
421                addArg = FALSE;
422                break;
423
424            case 'u':
425                tr_bencDictAddStr( &top, "method", "session-set" );
426                tr_bencDictAddInt( args, "speed-limit-up", numarg( optarg ) );
427                tr_bencDictAddInt( args, "speed-limit-up-enabled", 1 );
428                break;
429
430            case 'U':
431                tr_bencDictAddStr( &top, "method", "session-set" );
432                tr_bencDictAddInt( args, "speed-limit-up-enabled", 0 );
433                break;
434
435            case 'v':
436                tr_bencDictAddStr( &top, "method", "torrent-verify" );
437                addIdArg( args, id );
438                break;
439
440            case 'V':
441                fprintf(stderr, "Transmission %s\n", LONG_VERSION_STRING);
442                exit(0);
443                break;
444
445            case 'w': {
446                char * path = absolutify( optarg );
447                tr_bencDictAddStr( &top, "method", "session-set" );
448                tr_bencDictAddStr( args, "download-dir", path );
449                tr_free( path );
450                break;
451            }
452
453            case 'x':
454                tr_bencDictAddStr( &top, "method", "session-set" );
455                tr_bencDictAddInt( args, "pex-allowed", 1 );
456                break;
457
458            case 'X':
459                tr_bencDictAddStr( &top, "method", "session-set" );
460                tr_bencDictAddInt( args, "pex-allowed", 0 );
461                break;
462
463            case 'z':
464                tr_bencDictAddStr( &top, "method", "torrent-get" );
465                tr_bencDictAddInt( &top, "tag", TAG_PEERS );
466                addIdArg( args, id );
467                fields = tr_bencDictAddList( args, "fields", 1 );
468                tr_bencListAddStr( fields, "peers" );
469                break;
470
471            case 900:
472                tr_bencDictAddStr( &top, "method", "torrent-set" );
473                addIdArg( args, id );
474                addFiles( args, "priority-high", optarg );
475                break;
476
477            case 901:
478                tr_bencDictAddStr( &top, "method", "torrent-set" );
479                addIdArg( args, id );
480                addFiles( args, "priority-normal", optarg );
481                break;
482
483            case 902:
484                tr_bencDictAddStr( &top, "method", "torrent-set" );
485                addIdArg( args, id );
486                addFiles( args, "priority-low", optarg );
487                break;
488
489            case 910:
490                tr_bencDictAddStr( &top, "method", "session-set" );
491                tr_bencDictAddStr( args, "encryption", "required" );
492                break;
493
494            case 911:
495                tr_bencDictAddStr( &top, "method", "session-set" );
496                tr_bencDictAddStr( args, "encryption", "preferred" );
497                break;
498
499            case 912:
500                tr_bencDictAddStr( &top, "method", "session-set" );
501                tr_bencDictAddStr( args, "encryption", "tolerated" );
502                break;
503
504            case 920:
505                tr_bencDictAddStr( &top, "method", "session-get" );
506                tr_bencDictAddInt( &top, "tag", TAG_SESSION );
507                break;
508
509            case TR_OPT_ERR:
510                fprintf( stderr, "invalid option\n" );
511                showUsage( );
512                break;
513
514            default:
515                fprintf( stderr, "got opt [%d]\n", (int)c );
516                showUsage( );
517                break;
518        }
519
520        if( addArg )
521        {
522            struct evbuffer * buf = tr_getBuffer( );
523            reqs[reqCount++] = tr_strdup( tr_bencSaveAsJSON( &top, buf ) );
524            tr_releaseBuffer( buf );
525        }
526
527        tr_bencFree( &top );
528    }
529}
530
531/* [host:port] or [host] or [port] */
532static void
533getHostAndPort( int *   argc,
534                char ** argv,
535                char ** host,
536                int *   port )
537{
538    if( *argv[1] != '-' )
539    {
540        int          i;
541        const char * s = argv[1];
542        const char * delim = strchr( s, ':' );
543        if( delim )   /* user passed in both host and port */
544        {
545            *host = tr_strndup( s, delim - s );
546            *port = atoi( delim + 1 );
547        }
548        else
549        {
550            char *    end;
551            const int i = strtol( s, &end, 10 );
552            if( !*end ) /* user passed in a port */
553                *port = i;
554            else /* user passed in a host */
555                *host = tr_strdup( s );
556        }
557
558        *argc -= 1;
559        for( i = 1; i < *argc; ++i )
560            argv[i] = argv[i + 1];
561    }
562}
563
564static size_t
565writeFunc( void * ptr,
566           size_t size,
567           size_t nmemb,
568           void * buf )
569{
570    const size_t byteCount = size * nmemb;
571
572    evbuffer_add( buf, ptr, byteCount );
573    return byteCount;
574}
575
576static void
577etaToString( char *  buf,
578             size_t  buflen,
579             int64_t eta )
580{
581    if( eta < 0 ) tr_snprintf( buf, buflen, "Unknown" );
582    else if( eta < 60 ) tr_snprintf( buf, buflen, "%" PRId64 "sec", eta );
583    else if( eta <
584            ( 60 * 60 ) ) tr_snprintf( buf, buflen, "%" PRId64 " min",
585                                       eta / 60 );
586    else if( eta <
587            ( 60 * 60 * 24 ) ) tr_snprintf( buf, buflen, "%" PRId64 " hrs",
588                                           eta / ( 60 * 60 ) );
589    else tr_snprintf( buf, buflen, "%" PRId64 " days", eta / ( 60 * 60 * 24 ) );
590}
591
592#define KILOBYTE_FACTOR 1024.0
593#define MEGABYTE_FACTOR ( 1024.0 * 1024.0 )
594#define GIGABYTE_FACTOR ( 1024.0 * 1024.0 * 1024.0 )
595
596static char*
597strlratio2( char * buf, double ratio, size_t buflen )
598{
599    if( (int)ratio == TR_RATIO_NA )
600        tr_strlcpy( buf, "None", buflen );
601    else if( (int)ratio == TR_RATIO_INF )
602        tr_strlcpy( buf, "Inf", buflen );
603    else if( ratio < 10.0 )
604        tr_snprintf( buf, buflen, "%'.2f", ratio );
605    else if( ratio < 100.0 )
606        tr_snprintf( buf, buflen, "%'.1f", ratio );
607    else
608        tr_snprintf( buf, buflen, "%'.0f", ratio );
609    return buf;
610}
611
612static char*
613strlratio( char * buf,
614           double numerator,
615           double denominator,
616           size_t buflen )
617{
618    double ratio;
619
620    if( denominator )
621        ratio = numerator / denominator;
622    else if( numerator )
623        ratio = TR_RATIO_INF;
624    else
625        ratio = TR_RATIO_NA;
626
627    return strlratio2( buf, ratio, buflen );
628}
629
630static char*
631strlsize( char *  buf,
632          int64_t size,
633          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  %-8s  %-6s  %-6s  %-5s  %-11s  %s\n",
1081                "ID", "Done", "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
1104                if( leftUntilDone )
1105                    etaToString( etaStr, sizeof( etaStr ), eta );
1106                else
1107                    tr_snprintf( etaStr, sizeof( etaStr ), "Done" );
1108                printf(
1109                    "%4d  %3d%%  %-8s  %6.1f  %6.1f  %5s  %-11s  %s\n",
1110                    (int)id,
1111                    (int)( 100.0 *
1112                           ( sizeWhenDone - leftUntilDone ) / sizeWhenDone ),
1113                    etaStr,
1114                    up / 1024.0,
1115                    down / 1024.0,
1116                    strlratio2( ratioStr, ratio, sizeof( ratioStr ) ),
1117                    getStatusString( d, statusStr, sizeof( statusStr ) ),
1118                    name );
1119            }
1120        }
1121    }
1122}
1123
1124static void
1125processResponse( const char * host,
1126                 int          port,
1127                 const void * response,
1128                 size_t       len )
1129{
1130    tr_benc top;
1131
1132    if( debug )
1133        fprintf( stderr, "got response:\n--------\n%*.*s\n--------\n",
1134                 (int)len, (int)len, (const char*) response );
1135
1136    if( tr_jsonParse( response, len, &top, NULL ) )
1137        tr_nerr( MY_NAME, "Unable to parse response \"%*.*s\"", (int)len,
1138                 (int)len, (char*)response );
1139    else
1140    {
1141        int64_t      tag = -1;
1142        const char * str;
1143        tr_bencDictFindInt( &top, "tag", &tag );
1144
1145        switch( tag )
1146        {
1147            case TAG_SESSION:
1148                printSession( &top ); break;
1149
1150            case TAG_FILES:
1151                printFileList( &top ); break;
1152
1153            case TAG_DETAILS:
1154                printDetails( &top ); break;
1155
1156            case TAG_LIST:
1157                printTorrentList( &top ); break;
1158
1159            case TAG_PEERS:
1160                printPeers( &top ); break;
1161
1162            default:
1163                if( tr_bencDictFindStr( &top, "result", &str ) )
1164                    printf( "%s:%d responded: \"%s\"\n", host, port, str );
1165        }
1166
1167        tr_bencFree( &top );
1168    }
1169}
1170
1171static void
1172processRequests( const char *  host,
1173                 int           port,
1174                 const char ** reqs,
1175                 int           reqCount )
1176{
1177    int               i;
1178    CURL *            curl;
1179    struct evbuffer * buf = evbuffer_new( );
1180    char *            url = tr_strdup_printf(
1181        "http://%s:%d/transmission/rpc", host, port );
1182
1183    curl = curl_easy_init( );
1184    curl_easy_setopt( curl, CURLOPT_VERBOSE, debug );
1185#ifdef HAVE_LIBZ
1186    curl_easy_setopt( curl, CURLOPT_ENCODING, "deflate" );
1187#endif
1188    curl_easy_setopt( curl, CURLOPT_USERAGENT,
1189                      MY_NAME "/" LONG_VERSION_STRING );
1190    curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, writeFunc );
1191    curl_easy_setopt( curl, CURLOPT_WRITEDATA, buf );
1192    curl_easy_setopt( curl, CURLOPT_POST, 1 );
1193    curl_easy_setopt( curl, CURLOPT_URL, url );
1194    if( auth )
1195    {
1196        curl_easy_setopt( curl, CURLOPT_USERPWD, auth );
1197        curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
1198    }
1199
1200    for( i = 0; i < reqCount; ++i )
1201    {
1202        CURLcode res;
1203        curl_easy_setopt( curl, CURLOPT_POSTFIELDS, reqs[i] );
1204        if( debug )
1205            fprintf( stderr, "posting:\n--------\n%s\n--------\n", reqs[i] );
1206        if( ( res = curl_easy_perform( curl ) ) )
1207            tr_nerr( MY_NAME, "(%s:%d) %s", host, port,
1208                    curl_easy_strerror( res ) );
1209        else
1210            processResponse( host, port, EVBUFFER_DATA(
1211                                buf ), EVBUFFER_LENGTH( buf ) );
1212
1213        evbuffer_drain( buf, EVBUFFER_LENGTH( buf ) );
1214    }
1215
1216    /* cleanup */
1217    tr_free( url );
1218    evbuffer_free( buf );
1219    curl_easy_cleanup( curl );
1220}
1221
1222int
1223main( int     argc,
1224      char ** argv )
1225{
1226    int    i;
1227    int    port = DEFAULT_PORT;
1228    char * host = NULL;
1229
1230    if( argc < 2 )
1231        showUsage( );
1232
1233    getHostAndPort( &argc, argv, &host, &port );
1234    if( host == NULL )
1235        host = tr_strdup( DEFAULT_HOST );
1236
1237    readargs( argc, (const char**)argv );
1238    if( reqCount )
1239        processRequests( host, port, (const char**)reqs, reqCount );
1240    else
1241        showUsage( );
1242
1243    for( i = 0; i < reqCount; ++i )
1244        tr_free( reqs[i] );
1245
1246    tr_free( host );
1247    return 0;
1248}
1249
Note: See TracBrowser for help on using the repository browser.