source: trunk/daemon/remote.c @ 7002

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

(daemon) tweak the r7000 text a bit

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