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

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

(1.5x daemon) #1800: Display glitch in "transmission-daemon -l" when all files in a torrent are disabled

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