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

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

(1.5x daemon): #1899: if the daemon isn't responding, timeout transmission-remote after 60 seconds.

  • Property svn:keywords set to Date Rev Author Id
File size: 43.3 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 8044 2009-03-10 00:10:21Z 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
801static void
802printDetails( tr_benc * top )
803{
804    tr_benc *args, *torrents;
805
806    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
807      && ( tr_bencDictFindList( args, "torrents", &torrents ) ) )
808    {
809        int ti, tCount;
810        for( ti = 0, tCount = tr_bencListSize( torrents ); ti < tCount;
811             ++ti )
812        {
813            tr_benc *    t = tr_bencListChild( torrents, ti );
814            tr_benc *    l;
815            const char * str;
816            char         buf[512];
817            char         buf2[512];
818            int64_t      i, j, k;
819            tr_bool      isStopped;
820
821            isStopped = tr_bencDictFindInt( t, "status", &i ) && (i==TR_STATUS_STOPPED);
822
823            printf( "NAME\n" );
824            if( tr_bencDictFindInt( t, "id", &i ) )
825                printf( "  Id: %" PRId64 "\n", i );
826            if( tr_bencDictFindStr( t, "name", &str ) )
827                printf( "  Name: %s\n", str );
828            if( tr_bencDictFindStr( t, "hashString", &str ) )
829                printf( "  Hash: %s\n", str );
830            printf( "\n" );
831
832            printf( "TRANSFER\n" );
833            getStatusString( t, buf, sizeof( buf ) );
834            printf( "  State: %s\n", buf );
835
836            if( tr_bencDictFindStr( t, "downloadDir", &str ) )
837                printf( "  Location: %s\n", str );
838
839            if( tr_bencDictFindInt( t, "sizeWhenDone", &i )
840              && tr_bencDictFindInt( t, "leftUntilDone", &j ) )
841            {
842                strlratio( buf, 100.0 * ( i - j ), i, sizeof( buf ) );
843                printf( "  Percent Done: %s%%\n", buf );
844            }
845
846            if( tr_bencDictFindInt( t, "eta", &i ) )
847            {
848                etaToString( buf, sizeof( buf ), i );
849                printf( "  ETA: %s\n", buf );
850            }
851            if( tr_bencDictFindInt( t, "rateDownload", &i ) )
852                printf( "  Download Speed: %.1f KB/s\n", i / 1024.0 );
853            if( tr_bencDictFindInt( t, "rateUpload", &i ) )
854                printf( "  Upload Speed: %.1f KB/s\n", i / 1024.0 );
855            if( tr_bencDictFindInt( t, "haveUnchecked", &i )
856              && tr_bencDictFindInt( t, "haveValid", &j ) )
857            {
858                strlsize( buf, i + j, sizeof( buf ) );
859                strlsize( buf2, j, sizeof( buf2 ) );
860                printf( "  Have: %s (%s verified)\n", buf, buf2 );
861            }
862
863            if( tr_bencDictFindInt( t, "sizeWhenDone", &i )
864              && tr_bencDictFindInt( t, "totalSize", &j ) )
865            {
866                strlsize( buf, j, sizeof( buf ) );
867                strlsize( buf2, i, sizeof( buf2 ) );
868                printf( "  Total size: %s (%s wanted)\n", buf, buf2 );
869            }
870            if( tr_bencDictFindInt( t, "downloadedEver", &i )
871              && tr_bencDictFindInt( t, "uploadedEver", &j ) )
872            {
873                strlsize( buf, i, sizeof( buf ) );
874                printf( "  Downloaded: %s\n", buf );
875                strlsize( buf, j, sizeof( buf ) );
876                printf( "  Uploaded: %s\n", buf );
877                strlratio( buf, j, i, sizeof( buf ) );
878                printf( "  Ratio: %s\n", buf );
879            }
880            if( tr_bencDictFindInt( t, "corruptEver", &i ) )
881            {
882                strlsize( buf, i, sizeof( buf ) );
883                printf( "  Corrupt DL: %s\n", buf );
884            }
885            if( tr_bencDictFindStr( t, "errorString", &str ) && str && *str )
886                printf( "  Error: %s\n", str );
887
888            if( tr_bencDictFindInt( t, "peersConnected", &i )
889              && tr_bencDictFindInt( t, "peersGettingFromUs", &j )
890              && tr_bencDictFindInt( t, "peersSendingToUs", &k ) )
891            {
892                printf(
893                    "  Peers: "
894                    "connected to %" PRId64 ", "
895                                            "uploading to %" PRId64
896                    ", "
897                    "downloading from %"
898                    PRId64 "\n",
899                    i, j, k );
900            }
901
902            if( tr_bencDictFindList( t, "webseeds", &l )
903              && tr_bencDictFindInt( t, "webseedsSendingToUs", &i ) )
904            {
905                const int64_t n = tr_bencListSize( l );
906                if( n > 0 )
907                    printf(
908                        "  Web Seeds: downloading from %" PRId64 " of %"
909                        PRId64
910                        " web seeds\n", i, n );
911            }
912            printf( "\n" );
913
914            printf( "HISTORY\n" );
915            if( tr_bencDictFindInt( t, "addedDate", &i ) && i )
916            {
917                const time_t tt = i;
918                printf( "  Date added:      %s", ctime( &tt ) );
919            }
920            if( tr_bencDictFindInt( t, "doneDate", &i ) && i )
921            {
922                const time_t tt = i;
923                printf( "  Date finished:   %s", ctime( &tt ) );
924            }
925            if( tr_bencDictFindInt( t, "startDate", &i ) && i )
926            {
927                const time_t tt = i;
928                printf( "  Date started:    %s", ctime( &tt ) );
929            }
930            if( tr_bencDictFindInt( t, "activityDate", &i ) && i )
931            {
932                const time_t tt = i;
933                printf( "  Latest activity: %s", ctime( &tt ) );
934            }
935            printf( "\n" );
936
937            printf( "TRACKER\n" );
938            if( tr_bencDictFindInt( t, "lastAnnounceTime", &i ) )
939                printf( "  Latest announce: %s", getTrackerDateStr( (time_t)i, isStopped ) );
940            if( tr_bencDictFindStr( t, "announceURL", &str ) )
941                printf( "  Announce URL: %s\n", str );
942            if( tr_bencDictFindStr( t, "announceResponse", &str ) && str && *str )
943                printf( "  Announce response: %s\n", str );
944            if( tr_bencDictFindInt( t, "nextAnnounceTime", &i ) )
945                printf( "  Next announce:   %s", getTrackerDateStr( (time_t)i, isStopped ) );
946            if( tr_bencDictFindInt( t, "lastScrapeTime", &i ) )
947                printf( "  Latest scrape:   %s", getTrackerDateStr( (time_t)i, isStopped ) );
948            if( tr_bencDictFindStr( t, "scrapeResponse", &str ) )
949                printf( "  Scrape response: %s\n", str );
950            if( tr_bencDictFindInt( t, "nextScrapeTime", &i ) )
951                printf( "  Next scrape:     %s", getTrackerDateStr( (time_t)i, isStopped ) );
952            if( tr_bencDictFindInt( t, "seeders", &i ) && tr_bencDictFindInt( t, "leechers", &j ) )
953                printf( "  Tracker knows of %" PRId64 " seeders and %" PRId64 " leechers\n", i, j );
954            if( tr_bencDictFindInt( t, "timesCompleted", &i ) )
955                printf( "  Tracker has seen %" PRId64 " clients complete this torrent\n", i );
956            printf( "\n" );
957
958            printf( "ORIGINS\n" );
959            if( tr_bencDictFindInt( t, "dateCreated", &i ) && i )
960            {
961                const time_t tt = i;
962                printf( "  Date created: %s", ctime( &tt ) );
963            }
964            if( tr_bencDictFindInt( t, "isPrivate", &i ) )
965                printf( "  Public torrent: %s\n", ( i ? "No" : "Yes" ) );
966            if( tr_bencDictFindStr( t, "comment", &str ) && str && *str )
967                printf( "  Comment: %s\n", str );
968            if( tr_bencDictFindStr( t, "creator", &str ) && str && *str )
969                printf( "  Creator: %s\n", str );
970            if( tr_bencDictFindInt( t, "pieceCount", &i ) )
971                printf( "  Piece Count: %" PRId64 "\n", i );
972            if( tr_bencDictFindInt( t, "pieceSize", &i ) )
973                printf( "  Piece Size: %" PRId64 "\n", i );
974        }
975    }
976}
977
978static void
979printFileList( tr_benc * top )
980{
981    tr_benc *args, *torrents;
982
983    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
984      && ( tr_bencDictFindList( args, "torrents", &torrents ) ) )
985    {
986        int i, in;
987        for( i = 0, in = tr_bencListSize( torrents ); i < in; ++i )
988        {
989            tr_benc *    d = tr_bencListChild( torrents, i );
990            tr_benc *    files, *priorities, *wanteds;
991            const char * name;
992            if( tr_bencDictFindStr( d, "name", &name )
993              && tr_bencDictFindList( d, "files", &files )
994              && tr_bencDictFindList( d, "priorities", &priorities )
995              && tr_bencDictFindList( d, "wanted", &wanteds ) )
996            {
997                int j = 0, jn = tr_bencListSize( files );
998                printf( "%s (%d files):\n", name, jn );
999                printf( "%3s  %4s %8s %3s %9s  %s\n", "#", "Done",
1000                        "Priority", "Get", "Size",
1001                        "Name" );
1002                for( j = 0, jn = tr_bencListSize( files ); j < jn; ++j )
1003                {
1004                    int64_t      have;
1005                    int64_t      length;
1006                    int64_t      priority;
1007                    int64_t      wanted;
1008                    const char * filename;
1009                    tr_benc *    file = tr_bencListChild( files, j );
1010                    if( tr_bencDictFindInt( file, "length", &length )
1011                      && tr_bencDictFindStr( file, "name", &filename )
1012                      && tr_bencDictFindInt( file, "bytesCompleted", &have )
1013                      && tr_bencGetInt( tr_bencListChild( priorities,
1014                                                          j ), &priority )
1015                      && tr_bencGetInt( tr_bencListChild( wanteds,
1016                                                          j ), &wanted ) )
1017                    {
1018                        char         sizestr[64];
1019                        double       percent = (double)have / length;
1020                        const char * pristr;
1021                        strlsize( sizestr, length, sizeof( sizestr ) );
1022                        switch( priority )
1023                        {
1024                            case TR_PRI_LOW:
1025                                pristr = "Low"; break;
1026
1027                            case TR_PRI_HIGH:
1028                                pristr = "High"; break;
1029
1030                            default:
1031                                pristr = "Normal"; break;
1032                        }
1033                        printf( "%3d: %3.0f%% %-8s %-3s %9s  %s\n",
1034                                j,
1035                                ( 100.0 * percent ),
1036                                pristr,
1037                                ( wanted ? "Yes" : "No" ),
1038                                sizestr,
1039                                filename );
1040                    }
1041                }
1042            }
1043        }
1044    }
1045}
1046
1047static void
1048printPeersImpl( tr_benc * peers )
1049{
1050    int i, n;
1051    printf( "%-20s  %-12s  %-5s %-6s  %-6s  %s\n",
1052            "Address", "Flags", "Done", "Down", "Up", "Client" );
1053    for( i = 0, n = tr_bencListSize( peers ); i < n; ++i )
1054    {
1055        double progress;
1056        const char * address, * client, * flagstr;
1057        int64_t rateToClient, rateToPeer;
1058        tr_benc * d = tr_bencListChild( peers, i );
1059
1060        if( tr_bencDictFindStr( d, "address", &address )
1061          && tr_bencDictFindStr( d, "clientName", &client )
1062          && tr_bencDictFindDouble( d, "progress", &progress )
1063          && tr_bencDictFindStr( d, "flagStr", &flagstr )
1064          && tr_bencDictFindInt( d, "rateToClient", &rateToClient )
1065          && tr_bencDictFindInt( d, "rateToPeer", &rateToPeer ) )
1066        {
1067            printf( "%-20s  %-12s  %-5.1f %6.1f  %6.1f  %s\n",
1068                    address, flagstr, (progress*100.0),
1069                    rateToClient / 1024.0,
1070                    rateToPeer / 1024.0,
1071                    client );
1072        }
1073    }
1074}
1075
1076static void
1077printPeers( tr_benc * top )
1078{
1079    tr_benc *args, *torrents;
1080
1081    if( tr_bencDictFindDict( top, "arguments", &args )
1082      && tr_bencDictFindList( args, "torrents", &torrents ) )
1083    {
1084        int i, n;
1085        for( i=0, n=tr_bencListSize( torrents ); i<n; ++i )
1086        {
1087            tr_benc * peers;
1088            tr_benc * torrent = tr_bencListChild( torrents, i );
1089            if( tr_bencDictFindList( torrent, "peers", &peers ) ) {
1090                printPeersImpl( peers );
1091                if( i+1<n )
1092                    printf( "\n" );
1093            }
1094        }
1095    }
1096}
1097
1098static void
1099printTorrentList( tr_benc * top )
1100{
1101    tr_benc *args, *list;
1102
1103    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
1104      && ( tr_bencDictFindList( args, "torrents", &list ) ) )
1105    {
1106        int i, n;
1107        int64_t total_up = 0, total_down = 0, total_size = 0;
1108        char haveStr[32];
1109
1110        printf( "%-4s   %-4s  %9s  %-8s  %6s  %6s  %-5s  %-11s  %s\n",
1111                "ID", "Done", "Have", "ETA", "Up", "Down", "Ratio", "Status",
1112                "Name" );
1113        for( i = 0, n = tr_bencListSize( list ); i < n; ++i )
1114        {
1115            int64_t      id, eta, status, up, down;
1116            int64_t      sizeWhenDone, leftUntilDone;
1117            double       ratio;
1118            const char * name;
1119            tr_benc *   d = tr_bencListChild( list, i );
1120            if( tr_bencDictFindInt( d, "eta", &eta )
1121              && tr_bencDictFindInt( d, "id", &id )
1122              && tr_bencDictFindInt( d, "leftUntilDone", &leftUntilDone )
1123              && tr_bencDictFindStr( d, "name", &name )
1124              && tr_bencDictFindInt( d, "rateDownload", &down )
1125              && tr_bencDictFindInt( d, "rateUpload", &up )
1126              && tr_bencDictFindInt( d, "sizeWhenDone", &sizeWhenDone )
1127              && tr_bencDictFindInt( d, "status", &status )
1128              && tr_bencDictFindDouble( d, "uploadRatio", &ratio ) )
1129            {
1130                char etaStr[16];
1131                char statusStr[64];
1132                char ratioStr[32];
1133                char doneStr[8];
1134                int64_t error;
1135                char errorMark;
1136
1137                if( sizeWhenDone )
1138                    tr_snprintf( doneStr, sizeof( doneStr ), "%d%%", (int)( 100.0 * ( sizeWhenDone - leftUntilDone ) / sizeWhenDone ) );
1139                else
1140                    tr_strlcpy( doneStr, "n/a", sizeof( doneStr ) );
1141
1142                strlsize( haveStr, sizeWhenDone - leftUntilDone, sizeof( haveStr ) );
1143
1144                if( leftUntilDone )
1145                    etaToString( etaStr, sizeof( etaStr ), eta );
1146                else
1147                    tr_snprintf( etaStr, sizeof( etaStr ), "Done" );
1148                if( tr_bencDictFindInt( d, "error", &error ) && error )
1149                    errorMark = '*';
1150                else
1151                    errorMark = ' ';
1152                printf(
1153                    "%4d%c  %4s  %9s  %-8s  %6.1f  %6.1f  %5s  %-11s  %s\n",
1154                    (int)id, errorMark,
1155                    doneStr,
1156                    haveStr,
1157                    etaStr,
1158                    up / 1024.0,
1159                    down / 1024.0,
1160                    strlratio2( ratioStr, ratio, sizeof( ratioStr ) ),
1161                    getStatusString( d, statusStr, sizeof( statusStr ) ),
1162                    name );
1163
1164                total_up += up;
1165                total_down += down;
1166                total_size += sizeWhenDone - leftUntilDone;
1167            }
1168        }
1169
1170        printf( "Sum:         %9s             %6.1f  %6.1f\n",
1171                strlsize( haveStr, total_size, sizeof( haveStr ) ),
1172                total_up / 1024.0,
1173                total_down / 1024.0 );
1174    }
1175}
1176
1177static void
1178processResponse( const char * host,
1179                 int          port,
1180                 const void * response,
1181                 size_t       len )
1182{
1183    tr_benc top;
1184
1185    if( debug )
1186        fprintf( stderr, "got response:\n--------\n%*.*s\n--------\n",
1187                 (int)len, (int)len, (const char*) response );
1188
1189    if( tr_jsonParse( response, len, &top, NULL ) )
1190        tr_nerr( MY_NAME, "Unable to parse response \"%*.*s\"", (int)len,
1191                 (int)len, (char*)response );
1192    else
1193    {
1194        int64_t      tag = -1;
1195        const char * str;
1196        tr_bencDictFindInt( &top, "tag", &tag );
1197
1198        switch( tag )
1199        {
1200            case TAG_SESSION:
1201                printSession( &top ); break;
1202
1203            case TAG_FILES:
1204                printFileList( &top ); break;
1205
1206            case TAG_DETAILS:
1207                printDetails( &top ); break;
1208
1209            case TAG_LIST:
1210                printTorrentList( &top ); break;
1211
1212            case TAG_PEERS:
1213                printPeers( &top ); break;
1214
1215            default:
1216                if( tr_bencDictFindStr( &top, "result", &str ) )
1217                    printf( "%s:%d responded: \"%s\"\n", host, port, str );
1218        }
1219
1220        tr_bencFree( &top );
1221    }
1222}
1223
1224static void
1225processRequests( const char *  host,
1226                 int           port,
1227                 const char ** reqs,
1228                 int           reqCount )
1229{
1230    int               i;
1231    CURL *            curl;
1232    struct evbuffer * buf = evbuffer_new( );
1233    char *            url = tr_strdup_printf(
1234        "http://%s:%d/transmission/rpc", host, port );
1235
1236    curl = curl_easy_init( );
1237    curl_easy_setopt( curl, CURLOPT_VERBOSE, debug );
1238#ifdef HAVE_LIBZ
1239    curl_easy_setopt( curl, CURLOPT_ENCODING, "deflate" );
1240#endif
1241    curl_easy_setopt( curl, CURLOPT_USERAGENT, MY_NAME "/" LONG_VERSION_STRING );
1242    curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, writeFunc );
1243    curl_easy_setopt( curl, CURLOPT_WRITEDATA, buf );
1244    curl_easy_setopt( curl, CURLOPT_POST, 1 );
1245    curl_easy_setopt( curl, CURLOPT_URL, url );
1246    curl_easy_setopt( curl, CURLOPT_NETRC, CURL_NETRC_OPTIONAL );
1247    curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
1248    curl_easy_setopt( curl, CURLOPT_TIMEOUT, 60L );
1249    if( netrc )
1250        curl_easy_setopt( curl, CURLOPT_NETRC_FILE, netrc );
1251    if( auth )
1252        curl_easy_setopt( curl, CURLOPT_USERPWD, auth );
1253
1254    for( i = 0; i < reqCount; ++i )
1255    {
1256        CURLcode res;
1257        curl_easy_setopt( curl, CURLOPT_POSTFIELDS, reqs[i] );
1258        if( debug )
1259            fprintf( stderr, "posting:\n--------\n%s\n--------\n", reqs[i] );
1260        if( ( res = curl_easy_perform( curl ) ) )
1261            tr_nerr( MY_NAME, "(%s:%d) %s", host, port,
1262                    curl_easy_strerror( res ) );
1263        else
1264            processResponse( host, port, EVBUFFER_DATA(
1265                                buf ), EVBUFFER_LENGTH( buf ) );
1266
1267        evbuffer_drain( buf, EVBUFFER_LENGTH( buf ) );
1268    }
1269
1270    /* cleanup */
1271    tr_free( url );
1272    evbuffer_free( buf );
1273    curl_easy_cleanup( curl );
1274}
1275
1276int
1277main( int     argc,
1278      char ** argv )
1279{
1280    int    i;
1281    int    port = DEFAULT_PORT;
1282    char * host = NULL;
1283
1284    if( argc < 2 )
1285        showUsage( );
1286
1287    getHostAndPort( &argc, argv, &host, &port );
1288    if( host == NULL )
1289        host = tr_strdup( DEFAULT_HOST );
1290
1291    readargs( argc, (const char**)argv );
1292    if( reqCount )
1293        processRequests( host, port, (const char**)reqs, reqCount );
1294    else
1295        showUsage( );
1296
1297    for( i = 0; i < reqCount; ++i )
1298        tr_free( reqs[i] );
1299
1300    tr_free( host );
1301    return 0;
1302}
1303
Note: See TracBrowser for help on using the repository browser.