source: trunk/daemon/remote.c @ 7858

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

(trunk libT) #1392: changing per-torrent peer limits via transmission-remote

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