source: trunk/daemon/remote.c @ 7552

Last change on this file since 7552 was 7552, checked in by charles, 12 years ago

(trunk libT) have tr_bencSaveAsJSON() use an evbuffer

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