source: trunk/daemon/remote.c @ 7865

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

(trunk libT) #1784: add .netrc support to transmission-remote

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