source: trunk/daemon/remote.c @ 6795

Last change on this file since 6795 was 6795, checked in by charles, 14 years ago

run libT, cli, daemon, gtk through the source-code formatter "uncrustify" as promised/threatened

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