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

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

(1.5x daemon) #1819: Indicate torrent error in list

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