source: trunk/daemon/remote.c @ 7331

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

(daemon) #1510: kysucix's patch to give an option to delete local data via RPC when removing a torrent.

  • Property svn:keywords set to Date Rev Author Id
File size: 38.9 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 7331 2008-12-09 17:01:49Z 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 TR_DEFAULT_RPC_PORT
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_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            reqs[reqCount++] = tr_bencSaveAsJSON( &top, NULL );
528        tr_bencFree( &top );
529    }
530}
531
532/* [host:port] or [host] or [port] */
533static void
534getHostAndPort( int *   argc,
535                char ** argv,
536                char ** host,
537                int *   port )
538{
539    if( *argv[1] != '-' )
540    {
541        int          i;
542        const char * s = argv[1];
543        const char * delim = strchr( s, ':' );
544        if( delim )   /* user passed in both host and port */
545        {
546            *host = tr_strndup( s, delim - s );
547            *port = atoi( delim + 1 );
548        }
549        else
550        {
551            char *    end;
552            const int i = strtol( s, &end, 10 );
553            if( !*end ) /* user passed in a port */
554                *port = i;
555            else /* user passed in a host */
556                *host = tr_strdup( s );
557        }
558
559        *argc -= 1;
560        for( i = 1; i < *argc; ++i )
561            argv[i] = argv[i + 1];
562    }
563}
564
565static size_t
566writeFunc( void * ptr,
567           size_t size,
568           size_t nmemb,
569           void * buf )
570{
571    const size_t byteCount = size * nmemb;
572
573    evbuffer_add( buf, ptr, byteCount );
574    return byteCount;
575}
576
577static void
578etaToString( char *  buf,
579             size_t  buflen,
580             int64_t eta )
581{
582    if( eta < 0 ) tr_snprintf( buf, buflen, "Unknown" );
583    else if( eta < 60 ) tr_snprintf( buf, buflen, "%" PRId64 "sec", eta );
584    else if( eta <
585            ( 60 * 60 ) ) tr_snprintf( buf, buflen, "%" PRId64 " min",
586                                       eta / 60 );
587    else if( eta <
588            ( 60 * 60 * 24 ) ) tr_snprintf( buf, buflen, "%" PRId64 " hrs",
589                                           eta / ( 60 * 60 ) );
590    else tr_snprintf( buf, buflen, "%" PRId64 " days", eta / ( 60 * 60 * 24 ) );
591}
592
593#define KILOBYTE_FACTOR 1024.0
594#define MEGABYTE_FACTOR ( 1024.0 * 1024.0 )
595#define GIGABYTE_FACTOR ( 1024.0 * 1024.0 * 1024.0 )
596
597static char*
598strlratio2( char * buf, double ratio, size_t buflen )
599{
600    if( (int)ratio == TR_RATIO_NA )
601        tr_strlcpy( buf, "None", buflen );
602    else if( (int)ratio == TR_RATIO_INF )
603        tr_strlcpy( buf, "Inf", buflen );
604    else if( ratio < 10.0 )
605        tr_snprintf( buf, buflen, "%'.2f", ratio );
606    else if( ratio < 100.0 )
607        tr_snprintf( buf, buflen, "%'.1f", ratio );
608    else
609        tr_snprintf( buf, buflen, "%'.0f", ratio );
610    return buf;
611}
612
613static char*
614strlratio( char * buf,
615           double numerator,
616           double denominator,
617           size_t buflen )
618{
619    double ratio;
620
621    if( denominator )
622        ratio = numerator / denominator;
623    else if( numerator )
624        ratio = TR_RATIO_INF;
625    else
626        ratio = TR_RATIO_NA;
627
628    return strlratio2( buf, ratio, buflen );
629}
630
631static char*
632strlsize( char *  buf,
633          int64_t size,
634          size_t  buflen )
635{
636    if( !size )
637        tr_strlcpy( buf, "None", buflen );
638    else if( size < (int64_t)KILOBYTE_FACTOR )
639        tr_snprintf( buf, buflen, "%'" PRId64 " bytes", (int64_t)size );
640    else
641    {
642        double displayed_size;
643        if( size < (int64_t)MEGABYTE_FACTOR )
644        {
645            displayed_size = (double) size / KILOBYTE_FACTOR;
646            tr_snprintf( buf, buflen, "%'.1f KB", displayed_size );
647        }
648        else if( size < (int64_t)GIGABYTE_FACTOR )
649        {
650            displayed_size = (double) size / MEGABYTE_FACTOR;
651            tr_snprintf( buf, buflen, "%'.1f MB", displayed_size );
652        }
653        else
654        {
655            displayed_size = (double) size / GIGABYTE_FACTOR;
656            tr_snprintf( buf, buflen, "%'.1f GB", displayed_size );
657        }
658    }
659    return buf;
660}
661
662static char*
663getStatusString( tr_benc * t, char * buf, size_t buflen )
664{
665    int64_t status;
666
667    if( !tr_bencDictFindInt( t, "status", &status ) )
668    {
669        *buf = '\0';
670    }
671    else switch( status )
672    {
673        case TR_STATUS_STOPPED:
674            tr_strlcpy( buf, "Stopped", buflen );
675            break;
676
677        case TR_STATUS_CHECK_WAIT:
678        case TR_STATUS_CHECK: {
679            const char * str = status == TR_STATUS_CHECK_WAIT
680                             ? "Will Verify"
681                             : "Verifying";
682            double percent;
683            if( tr_bencDictFindDouble( t, "recheckProgress", &percent ) )
684                tr_snprintf( buf, buflen, "%s (%.0f%%)", str, percent*100.0 );
685            else
686                tr_strlcpy( buf, str, buflen );
687
688            break;
689        }
690
691        case TR_STATUS_DOWNLOAD:
692        case TR_STATUS_SEED: {
693            int64_t fromUs = 0;
694            int64_t toUs = 0;
695            tr_bencDictFindInt( t, "peersGettingFromUs", &fromUs );
696            tr_bencDictFindInt( t, "peersSendingToUs", &toUs );
697            if( fromUs && toUs )
698                tr_strlcpy( buf, "Up & Down", buflen );
699            else if( toUs )
700                tr_strlcpy( buf, "Downloading", buflen );
701            else if( fromUs ) {
702                int64_t leftUntilDone = 0;
703                tr_bencDictFindInt( t, "leftUntilDone", &leftUntilDone );
704                if( leftUntilDone > 0 )
705                    tr_strlcpy( buf, "Uploading", buflen );
706                else
707                    tr_strlcpy( buf, "Seeding", buflen );
708            } else
709                tr_strlcpy( buf, "Idle", buflen );
710            break;
711        }
712    }
713
714    return buf;
715}
716
717static void
718printDetails( tr_benc * top )
719{
720    tr_benc *args, *torrents;
721
722    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
723      && ( tr_bencDictFindList( args, "torrents", &torrents ) ) )
724    {
725        int ti, tCount;
726        for( ti = 0, tCount = tr_bencListSize( torrents ); ti < tCount;
727             ++ti )
728        {
729            tr_benc *    t = tr_bencListChild( torrents, ti );
730            tr_benc *    l;
731            const char * str;
732            char         buf[512];
733            char         buf2[512];
734            int64_t      i, j, k;
735
736            printf( "NAME\n" );
737            if( tr_bencDictFindInt( t, "id", &i ) )
738                printf( "  Id: %" PRId64 "\n", i );
739            if( tr_bencDictFindStr( t, "name", &str ) )
740                printf( "  Name: %s\n", str );
741            if( tr_bencDictFindStr( t, "hashString", &str ) )
742                printf( "  Hash: %s\n", str );
743            printf( "\n" );
744
745            printf( "TRANSFER\n" );
746            getStatusString( t, buf, sizeof( buf ) );
747            printf( "  State: %s\n", buf );
748
749            if( tr_bencDictFindInt( t, "sizeWhenDone", &i )
750              && tr_bencDictFindInt( t, "leftUntilDone", &j ) )
751            {
752                strlratio( buf, 100.0 * ( i - j ), i, sizeof( buf ) );
753                printf( "  Percent Done: %s%%\n", buf );
754            }
755
756            if( tr_bencDictFindInt( t, "eta", &i ) )
757            {
758                etaToString( buf, sizeof( buf ), i );
759                printf( "  ETA: %s\n", buf );
760            }
761            if( tr_bencDictFindInt( t, "rateDownload", &i ) )
762                printf( "  Download Speed: %.1f KB/s\n", i / 1024.0 );
763            if( tr_bencDictFindInt( t, "rateUpload", &i ) )
764                printf( "  Upload Speed: %.1f KB/s\n", i / 1024.0 );
765            if( tr_bencDictFindInt( t, "haveUnchecked", &i )
766              && tr_bencDictFindInt( t, "haveValid", &j ) )
767            {
768                strlsize( buf, i + j, sizeof( buf ) );
769                strlsize( buf2, j, sizeof( buf2 ) );
770                printf( "  Have: %s (%s verified)\n", buf, buf2 );
771            }
772
773            if( tr_bencDictFindInt( t, "sizeWhenDone", &i )
774              && tr_bencDictFindInt( t, "totalSize", &j ) )
775            {
776                strlsize( buf, j, sizeof( buf ) );
777                strlsize( buf2, i, sizeof( buf2 ) );
778                printf( "  Total size: %s (%s wanted)\n", buf, buf2 );
779            }
780            if( tr_bencDictFindInt( t, "downloadedEver", &i )
781              && tr_bencDictFindInt( t, "uploadedEver", &j ) )
782            {
783                strlsize( buf, i, sizeof( buf ) );
784                printf( "  Downloaded: %s\n", buf );
785                strlsize( buf, j, sizeof( buf ) );
786                printf( "  Uploaded: %s\n", buf );
787                strlratio( buf, j, i, sizeof( buf ) );
788                printf( "  Ratio: %s\n", buf );
789            }
790            if( tr_bencDictFindInt( t, "corruptEver", &i ) )
791            {
792                strlsize( buf, i, sizeof( buf ) );
793                printf( "  Corrupt DL: %s\n", buf );
794            }
795            if( tr_bencDictFindStr( t, "errorString", &str ) && str && *str )
796                printf( "  Error: %s\n", str );
797
798            if( tr_bencDictFindInt( t, "peersConnected", &i )
799              && tr_bencDictFindInt( t, "peersGettingFromUs", &j )
800              && tr_bencDictFindInt( t, "peersSendingToUs", &k ) )
801            {
802                printf(
803                    "  Peers: "
804                    "connected to %" PRId64 ", "
805                                            "uploading to %" PRId64
806                    ", "
807                    "downloading from %"
808                    PRId64 "\n",
809                    i, j, k );
810            }
811
812            if( tr_bencDictFindList( t, "webseeds", &l )
813              && tr_bencDictFindInt( t, "webseedsSendingToUs", &i ) )
814            {
815                const int64_t n = tr_bencListSize( l );
816                if( n > 0 )
817                    printf(
818                        "  Web Seeds: downloading from %" PRId64 " of %"
819                        PRId64
820                        " web seeds\n", i, n );
821            }
822            printf( "\n" );
823
824            printf( "HISTORY\n" );
825            if( tr_bencDictFindInt( t, "addedDate", &i ) && i )
826            {
827                const time_t tt = i;
828                printf( "  Date added:      %s", ctime( &tt ) );
829            }
830            if( tr_bencDictFindInt( t, "doneDate", &i ) && i )
831            {
832                const time_t tt = i;
833                printf( "  Date finished:   %s", ctime( &tt ) );
834            }
835            if( tr_bencDictFindInt( t, "startDate", &i ) && i )
836            {
837                const time_t tt = i;
838                printf( "  Date started:    %s", ctime( &tt ) );
839            }
840            if( tr_bencDictFindInt( t, "activityDate", &i ) && i )
841            {
842                const time_t tt = i;
843                printf( "  Latest activity: %s", ctime( &tt ) );
844            }
845            printf( "\n" );
846
847            printf( "TRACKER\n" );
848            if( tr_bencDictFindInt( t, "lastAnnounceTime", &i ) && i )
849            {
850                const time_t tt = i;
851                printf( "  Latest announce: %s", ctime( &tt ) );
852            }
853            if( tr_bencDictFindStr( t, "announceURL", &str ) )
854                printf( "  Announce URL: %s\n", str );
855            if( tr_bencDictFindStr( t, "announceResponse",
856                                    &str ) && str && *str )
857                printf( "  Announce response: %s\n", str );
858            if( tr_bencDictFindInt( t, "nextAnnounceTime", &i ) && i )
859            {
860                const time_t tt = i;
861                printf( "  Next announce:   %s", ctime( &tt ) );
862            }
863            if( tr_bencDictFindInt( t, "lastScrapeTime", &i ) && i )
864            {
865                const time_t tt = i;
866                printf( "  Latest scrape:   %s", ctime( &tt ) );
867            }
868            if( tr_bencDictFindStr( t, "scrapeResponse", &str ) )
869                printf( "  Scrape response: %s\n", str );
870            if( tr_bencDictFindInt( t, "nextScrapeTime", &i ) && i )
871            {
872                const time_t tt = i;
873                printf( "  Next scrape:     %s", ctime( &tt ) );
874            }
875            if( tr_bencDictFindInt( t, "seeders", &i )
876              && tr_bencDictFindInt( t, "leechers", &j ) )
877                printf(
878                    "  Tracker knows of %" PRId64 " seeders and %" PRId64
879                    " leechers\n", i, j );
880            if( tr_bencDictFindInt( t, "timesCompleted", &i ) )
881                printf(
882                    "  Tracker has seen %" PRId64
883                    " clients complete this torrent\n", i );
884            printf( "\n" );
885
886            printf( "ORIGINS\n" );
887            if( tr_bencDictFindInt( t, "dateCreated", &i ) && i )
888            {
889                const time_t tt = i;
890                printf( "  Date created: %s", ctime( &tt ) );
891            }
892            if( tr_bencDictFindInt( t, "isPrivate", &i ) )
893                printf( "  Public torrent: %s\n", ( i ? "No" : "Yes" ) );
894            if( tr_bencDictFindStr( t, "comment", &str ) && str && *str )
895                printf( "  Comment: %s\n", str );
896            if( tr_bencDictFindStr( t, "creator", &str ) && str && *str )
897                printf( "  Creator: %s\n", str );
898            if( tr_bencDictFindInt( t, "pieceCount", &i ) )
899                printf( "  Piece Count: %" PRId64 "\n", i );
900            if( tr_bencDictFindInt( t, "pieceSize", &i ) )
901                printf( "  Piece Size: %" PRId64 "\n", i );
902        }
903    }
904}
905
906static void
907printFileList( tr_benc * top )
908{
909    tr_benc *args, *torrents;
910
911    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
912      && ( tr_bencDictFindList( args, "torrents", &torrents ) ) )
913    {
914        int i, in;
915        for( i = 0, in = tr_bencListSize( torrents ); i < in; ++i )
916        {
917            tr_benc *    d = tr_bencListChild( torrents, i );
918            tr_benc *    files, *priorities, *wanteds;
919            const char * name;
920            if( tr_bencDictFindStr( d, "name", &name )
921              && tr_bencDictFindList( d, "files", &files )
922              && tr_bencDictFindList( d, "priorities", &priorities )
923              && tr_bencDictFindList( d, "wanted", &wanteds ) )
924            {
925                int j = 0, jn = tr_bencListSize( files );
926                printf( "%s (%d files):\n", name, jn );
927                printf( "%3s  %4s %8s %3s %9s  %s\n", "#", "Done",
928                        "Priority", "Get", "Size",
929                        "Name" );
930                for( j = 0, jn = tr_bencListSize( files ); j < jn; ++j )
931                {
932                    int64_t      have;
933                    int64_t      length;
934                    int64_t      priority;
935                    int64_t      wanted;
936                    const char * filename;
937                    tr_benc *    file = tr_bencListChild( files, j );
938                    if( tr_bencDictFindInt( file, "length", &length )
939                      && tr_bencDictFindStr( file, "name", &filename )
940                      && tr_bencDictFindInt( file, "bytesCompleted", &have )
941                      && tr_bencGetInt( tr_bencListChild( priorities,
942                                                          j ), &priority )
943                      && tr_bencGetInt( tr_bencListChild( wanteds,
944                                                          j ), &wanted ) )
945                    {
946                        char         sizestr[64];
947                        double       percent = (double)have / length;
948                        const char * pristr;
949                        strlsize( sizestr, length, sizeof( sizestr ) );
950                        switch( priority )
951                        {
952                            case TR_PRI_LOW:
953                                pristr = "Low"; break;
954
955                            case TR_PRI_HIGH:
956                                pristr = "High"; break;
957
958                            default:
959                                pristr = "Normal"; break;
960                        }
961                        printf( "%3d: %3.0f%% %-8s %-3s %9s  %s\n",
962                                ( j + 1 ),
963                                ( 100.0 * percent ),
964                                pristr,
965                                ( wanted ? "Yes" : "No" ),
966                                sizestr,
967                                filename );
968                    }
969                }
970            }
971        }
972    }
973}
974
975static void
976printPeersImpl( tr_benc * peers )
977{
978    int i, n;
979    printf( "%-20s  %-12s  %-6s  %-6s  %s\n",
980            "Address", "Flags", "Down", "Up", "Client" );
981    for( i = 0, n = tr_bencListSize( peers ); i < n; ++i )
982    {
983        const char * address, * client, * flagstr;
984        int64_t      rateToClient, rateToPeer;
985        tr_benc *    d = tr_bencListChild( peers, i );
986        if( tr_bencDictFindStr( d, "address", &address )
987          && tr_bencDictFindStr( d, "clientName", &client )
988          && tr_bencDictFindStr( d, "flagStr", &flagstr )
989          && tr_bencDictFindInt( d, "rateToClient", &rateToClient )
990          && tr_bencDictFindInt( d, "rateToPeer", &rateToPeer ) )
991        {
992            printf( "%-20s  %-12s  %6.1f  %6.1f  %s\n",
993                    address, flagstr,
994                    rateToClient / 1024.0,
995                    rateToPeer / 1024.0,
996                    client );
997        }
998    }
999}
1000
1001static void
1002printPeers( tr_benc * top )
1003{
1004    tr_benc *args, *torrents;
1005
1006    if( tr_bencDictFindDict( top, "arguments", &args )
1007      && tr_bencDictFindList( args, "torrents", &torrents ) )
1008    {
1009        int i, n;
1010        for( i=0, n=tr_bencListSize( torrents ); i<n; ++i )
1011        {
1012            tr_benc * peers;
1013            tr_benc * torrent = tr_bencListChild( torrents, i );
1014            if( tr_bencDictFindList( torrent, "peers", &peers ) ) {
1015                printPeersImpl( peers );
1016                if( i+1<n )
1017                    printf( "\n" );
1018            }
1019        }
1020    }
1021}
1022
1023static void
1024printTorrentList( tr_benc * top )
1025{
1026    tr_benc *args, *list;
1027
1028    if( ( tr_bencDictFindDict( top, "arguments", &args ) )
1029      && ( tr_bencDictFindList( args, "torrents", &list ) ) )
1030    {
1031        int i, n;
1032        printf( "%-4s  %-4s  %-8s  %-6s  %-6s  %-5s  %-11s  %s\n",
1033                "ID", "Done", "ETA", "Up", "Down", "Ratio", "Status",
1034                "Name" );
1035        for( i = 0, n = tr_bencListSize( list ); i < n; ++i )
1036        {
1037            int64_t      id, eta, status, up, down;
1038            int64_t      sizeWhenDone, leftUntilDone;
1039            double       ratio;
1040            const char * name;
1041            tr_benc *   d = tr_bencListChild( list, i );
1042            if( tr_bencDictFindInt( d, "eta", &eta )
1043              && tr_bencDictFindInt( d, "id", &id )
1044              && tr_bencDictFindInt( d, "leftUntilDone", &leftUntilDone )
1045              && tr_bencDictFindStr( d, "name", &name )
1046              && tr_bencDictFindInt( d, "rateDownload", &down )
1047              && tr_bencDictFindInt( d, "rateUpload", &up )
1048              && tr_bencDictFindInt( d, "sizeWhenDone", &sizeWhenDone )
1049              && tr_bencDictFindInt( d, "status", &status )
1050              && tr_bencDictFindDouble( d, "uploadRatio", &ratio ) )
1051            {
1052                char etaStr[16];
1053                char statusStr[64];
1054                char ratioStr[32];
1055
1056                if( leftUntilDone )
1057                    etaToString( etaStr, sizeof( etaStr ), eta );
1058                else
1059                    tr_snprintf( etaStr, sizeof( etaStr ), "Done" );
1060                printf(
1061                    "%4d  %3d%%  %-8s  %6.1f  %6.1f  %5s  %-11s  %s\n",
1062                    (int)id,
1063                    (int)( 100.0 *
1064                           ( sizeWhenDone - leftUntilDone ) / sizeWhenDone ),
1065                    etaStr,
1066                    up / 1024.0,
1067                    down / 1024.0,
1068                    strlratio2( ratioStr, ratio, sizeof( ratioStr ) ),
1069                    getStatusString( d, statusStr, sizeof( statusStr ) ),
1070                    name );
1071            }
1072        }
1073    }
1074}
1075
1076static void
1077processResponse( const char * host,
1078                 int          port,
1079                 const void * response,
1080                 size_t       len )
1081{
1082    tr_benc top;
1083
1084    if( debug )
1085        fprintf( stderr, "got response:\n--------\n%*.*s\n--------\n",
1086                 (int)len, (int)len, (const char*) response );
1087
1088    if( tr_jsonParse( response, len, &top, NULL ) )
1089        tr_nerr( MY_NAME, "Unable to parse response \"%*.*s\"", (int)len,
1090                 (int)len, (char*)response );
1091    else
1092    {
1093        int64_t      tag = -1;
1094        const char * str;
1095        tr_bencDictFindInt( &top, "tag", &tag );
1096
1097        switch( tag )
1098        {
1099            case TAG_FILES:
1100                printFileList( &top ); break;
1101
1102            case TAG_DETAILS:
1103                printDetails( &top ); break;
1104
1105            case TAG_LIST:
1106                printTorrentList( &top ); break;
1107
1108            case TAG_PEERS:
1109                printPeers( &top ); break;
1110
1111            default:
1112                if( tr_bencDictFindStr( &top, "result", &str ) )
1113                    printf( "%s:%d responded: \"%s\"\n", host, port, str );
1114        }
1115
1116        tr_bencFree( &top );
1117    }
1118}
1119
1120static void
1121processRequests( const char *  host,
1122                 int           port,
1123                 const char ** reqs,
1124                 int           reqCount )
1125{
1126    int               i;
1127    CURL *            curl;
1128    struct evbuffer * buf = evbuffer_new( );
1129    char *            url = tr_strdup_printf(
1130        "http://%s:%d/transmission/rpc", host, port );
1131
1132    curl = curl_easy_init( );
1133    curl_easy_setopt( curl, CURLOPT_VERBOSE, debug );
1134#ifdef HAVE_LIBZ
1135    curl_easy_setopt( curl, CURLOPT_ENCODING, "deflate" );
1136#endif
1137    curl_easy_setopt( curl, CURLOPT_USERAGENT,
1138                      MY_NAME "/" LONG_VERSION_STRING );
1139    curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, writeFunc );
1140    curl_easy_setopt( curl, CURLOPT_WRITEDATA, buf );
1141    curl_easy_setopt( curl, CURLOPT_POST, 1 );
1142    curl_easy_setopt( curl, CURLOPT_URL, url );
1143    if( auth )
1144    {
1145        curl_easy_setopt( curl, CURLOPT_USERPWD, auth );
1146        curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
1147    }
1148
1149    for( i = 0; i < reqCount; ++i )
1150    {
1151        CURLcode res;
1152        curl_easy_setopt( curl, CURLOPT_POSTFIELDS, reqs[i] );
1153        if( debug )
1154            fprintf( stderr, "posting:\n--------\n%s\n--------\n", reqs[i] );
1155        if( ( res = curl_easy_perform( curl ) ) )
1156            tr_nerr( MY_NAME, "(%s:%d) %s", host, port,
1157                    curl_easy_strerror( res ) );
1158        else
1159            processResponse( host, port, EVBUFFER_DATA(
1160                                buf ), EVBUFFER_LENGTH( buf ) );
1161
1162        evbuffer_drain( buf, EVBUFFER_LENGTH( buf ) );
1163    }
1164
1165    /* cleanup */
1166    tr_free( url );
1167    evbuffer_free( buf );
1168    curl_easy_cleanup( curl );
1169}
1170
1171int
1172main( int     argc,
1173      char ** argv )
1174{
1175    int    i;
1176    int    port = DEFAULT_PORT;
1177    char * host = NULL;
1178
1179    if( argc < 2 )
1180        showUsage( );
1181
1182    getHostAndPort( &argc, argv, &host, &port );
1183    if( host == NULL )
1184        host = tr_strdup( DEFAULT_HOST );
1185
1186    readargs( argc, (const char**)argv );
1187    if( reqCount )
1188        processRequests( host, port, (const char**)reqs, reqCount );
1189    else
1190        showUsage( );
1191
1192    for( i = 0; i < reqCount; ++i )
1193        tr_free( reqs[i] );
1194
1195    tr_free( host );
1196    return 0;
1197}
1198
Note: See TracBrowser for help on using the repository browser.