source: trunk/daemon/remote.c @ 6313

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

(daemon) in remote's "info" feature, separate "progress" into "recheck progress" and "download progress".

  • Property svn:keywords set to Date Rev Author Id
File size: 27.2 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 6313 2008-07-10 17:19:43Z charles $
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h> /* strcmp */
16
17#include <unistd.h> /* getcwd */
18
19#include <libevent/event.h>
20#include <curl/curl.h>
21
22#include <libtransmission/transmission.h>
23#include <libtransmission/bencode.h>
24#include <libtransmission/rpc.h>
25#include <libtransmission/json.h>
26#include <libtransmission/tr-getopt.h>
27#include <libtransmission/utils.h>
28#include <libtransmission/version.h>
29
30#define MY_NAME "transmission-remote"
31#define DEFAULT_HOST "localhost"
32#define DEFAULT_PORT TR_DEFAULT_RPC_PORT
33
34enum { TAG_LIST, TAG_DETAILS, TAG_FILES };
35
36static const char*
37getUsage( void )
38{
39    return "Transmission "LONG_VERSION_STRING"  http://www.transmissionbt.com/\n"
40           "A fast and easy BitTorrent client\n"
41           "\n"
42           "Usage: "MY_NAME" [host] [options]\n"
43           "       "MY_NAME" [port] [options]\n"
44           "       "MY_NAME" [host:port] [options]";
45}
46
47static tr_option opts[] =
48{
49    { 'a', "add",          "Add torrent files", "a", 0, NULL },
50    { 'd', "downlimit",    "Set the maximum download speed in KB/s", "d", 1, "<number>" },
51    { 'D', "no-downlimit", "Don't limit the download speed", "D", 0, NULL },
52    { 'e', "encryption",   "Set encryption mode [required, preferred, tolerated]", "e", 1, "<mode>" },
53    { 'f', "files",        "Get a file list for the current torrent(s)", "f", 0, NULL },
54    { 'g', "debug",        "Print debugging information", "g", 0, NULL },
55    { 'h', "help",         "Show this help page and exit", "h", 0, NULL },
56    { 'i', "info",         "Show details of the current torrent(s)", "i", 0, NULL },
57    { 'l', "list",         "List all torrents", "l", 0, NULL },
58    { 'm', "portmap",      "Enable portmapping via NAT-PMP or UPnP", "m", 0, NULL },
59    { 'M', "no-portmap",   "Disable portmapping", "M", 0, NULL },
60    { 'n', "auth",         "Set username for authentication", "n", 1, "<user>:<pass>" },
61    { 'p', "port",         "Port to listen for incoming peers", "p", 1, "<port>" },
62    { 'r', "remove",       "Remove the current torrent(s)", "r", 0, NULL },
63    { 's', "start",        "Start the current torrent(s)", "s", 0, NULL },
64    { 'S', "stop",         "Stop the current torrent(s)", "S", 0, NULL },
65    { 't', "torrent",      "Set the current torrent(s)", "t", 1, "<id|hash|all>" },
66    { 'u', "uplimit",      "Set the maximum upload speed in KB/s", "u", 1, "<number>" },
67    { 'U', "no-uplimit",   "Don't limit the upload speed", "U", 0, NULL },
68    { 'v', "verify",       "Verify the current torrent(s)", "v", 0, NULL },
69    { 'w', "download-dir", "Set the download folder for new torrents", "w", 1, "<path>" },
70    { 'x', "pex",          "Enable peer exchange (PEX)", "x", 0, NULL },
71    { 'X', "no-pex",       "Disable peer exchange (PEX)", "X", 0, NULL },
72    { 0, NULL, NULL, NULL, 0, NULL }
73};
74
75static void
76showUsage( void )
77{
78    tr_getopt_usage( MY_NAME, getUsage(), opts );
79    exit( 0 );
80}
81
82static int
83numarg( const char * arg )
84{
85    char * end = NULL;
86    const long num = strtol( arg, &end, 10 );
87    if( *end ) {
88        fprintf( stderr, "Not a number: \"%s\"\n", arg );
89        showUsage( );
90    }
91    return num;
92}
93
94static char * reqs[256]; /* arbitrary max */
95static int reqCount = 0;
96static int debug = 0;
97static char * auth = NULL;
98
99static char*
100absolutify( char * buf, size_t len, const char * path )
101{
102    if( *path == '/' )
103        tr_strlcpy( buf, path, len );
104    else {
105        char cwd[MAX_PATH_LENGTH];
106        getcwd( cwd, sizeof( cwd ) );
107        tr_buildPath( buf, len, cwd, path, NULL );
108    }
109    return buf;
110}
111
112static char*
113getEncodedMetainfo( const char * filename )
114{
115    size_t len = 0;
116    uint8_t * buf = tr_loadFile( filename, &len );
117    char * b64 = tr_base64_encode( buf, len, NULL );
118    tr_free( buf );
119    return b64;
120}
121
122static void
123addIdArg( tr_benc * args, const char * id )
124{
125    if( !*id ) {
126        fprintf( stderr, "No torrent specified!  Please use the -t option first.\n" );
127        id = "-1"; /* no torrent will have this ID, so should be a no-op */
128    } else if( strcmp( id, "all" ) ) {
129        tr_rpc_parse_list_str( tr_bencDictAdd( args, "ids" ), id, strlen(id) );
130    }
131}
132
133static void
134readargs( int argc, const char ** argv )
135{
136    int c;
137    int addingTorrents = 0;
138    const char * optarg;
139    char id[4096];
140
141    *id = '\0';
142
143    while(( c = tr_getopt( getUsage(), argc, argv, opts, &optarg )))
144    {
145        char buf[MAX_PATH_LENGTH];
146        int addArg = TRUE;
147        tr_benc top, *args;
148        tr_bencInitDict( &top, 3 );
149        int64_t fields = 0;
150        args = tr_bencDictAddDict( &top, "arguments", 0 );
151
152        switch( c )
153        {
154            case TR_OPT_UNK:
155                      if( addingTorrents ) {
156                          char * tmp = getEncodedMetainfo( optarg );
157                          tr_bencDictAddStr( &top, "method", "torrent-add" );
158                          tr_bencDictAddStr( args, "metainfo", tmp );
159                          tr_free( tmp );
160                      } else {
161                          fprintf( stderr, "Unknown option: %s\n", optarg );
162                          addArg = FALSE;
163                      }
164                      break;
165            case 'a': addingTorrents = 1;
166                      addArg = FALSE;
167                      break;
168            case 'd': tr_bencDictAddStr( &top, "method", "session-set" );
169                      tr_bencDictAddInt( args, "speed-limit-down", numarg( optarg ) );
170                      tr_bencDictAddInt( args, "speed-limit-down-enabled", 1 );
171                      break;
172            case 'D': tr_bencDictAddStr( &top, "method", "session-set" );
173                      tr_bencDictAddInt( args, "speed-limit-down-enabled", 0 );
174                      break;
175            case 'e': tr_bencDictAddStr( &top, "method", "session-set" );
176                      tr_bencDictAddStr( args, "encryption", optarg );
177                      break;
178            case 'f': tr_bencDictAddStr( &top, "method", "torrent-get" );
179                      tr_bencDictAddInt( &top, "tag", TAG_FILES );
180                      addIdArg( args, id );
181                      fields = TR_RPC_TORRENT_ID
182                             | TR_RPC_TORRENT_FILES
183                             | TR_RPC_TORRENT_PRIORITIES;
184                      tr_bencDictAddInt( args, "fields", fields );
185                      break;
186            case 'g': debug = 1;
187                      addArg = FALSE;
188                      break;
189            case 'i': tr_bencDictAddStr( &top, "method", "torrent-get" );
190                      tr_bencDictAddInt( &top, "tag", TAG_DETAILS );
191                      addIdArg( args, id );
192                      fields = TR_RPC_TORRENT_ACTIVITY
193                          | TR_RPC_TORRENT_ANNOUNCE
194                          | TR_RPC_TORRENT_ERROR
195                          | TR_RPC_TORRENT_HISTORY
196                          | TR_RPC_TORRENT_ID
197                          | TR_RPC_TORRENT_INFO
198                          | TR_RPC_TORRENT_SCRAPE
199                          | TR_RPC_TORRENT_SIZE
200                          | TR_RPC_TORRENT_TRACKER_STATS;
201                      tr_bencDictAddInt( args, "fields", fields );
202                      break;
203            case 'l': tr_bencDictAddStr( &top, "method", "torrent-get" );
204                      tr_bencDictAddInt( &top, "tag", TAG_LIST );
205                      fields = TR_RPC_TORRENT_ID
206                             | TR_RPC_TORRENT_ACTIVITY
207                             | TR_RPC_TORRENT_HISTORY
208                             | TR_RPC_TORRENT_SIZE;
209                      tr_bencDictAddInt( args, "fields", fields );
210                      break;
211            case 'm': tr_bencDictAddStr( &top, "method", "session-set" );
212                      tr_bencDictAddInt( args, "port-forwarding-enabled", 1 );
213                      break;
214            case 'M': tr_bencDictAddStr( &top, "method", "session-set" );
215                      tr_bencDictAddInt( args, "port-forwarding-enabled", 0 );
216                      break;
217            case 'n': auth = tr_strdup( optarg );
218                      addArg = FALSE;
219                      break;
220            case 'p': tr_bencDictAddStr( &top, "method", "session-set" );
221                      tr_bencDictAddInt( args, "port", numarg( optarg ) );
222                      break;
223            case 'r': tr_bencDictAddStr( &top, "method", "torrent-remove" );
224                      addIdArg( args, id );
225                      break;
226            case 's': tr_bencDictAddStr( &top, "method", "torrent-start" );
227                      addIdArg( args, id );
228                      break;
229            case 'S': tr_bencDictAddStr( &top, "method", "torrent-stop" );
230                      addIdArg( args, id );
231                      break;
232            case 't': tr_strlcpy( id, optarg, sizeof( id ) );
233                      addArg = FALSE;
234                      break;
235            case 'u': tr_bencDictAddStr( &top, "method", "session-set" );
236                      tr_bencDictAddInt( args, "speed-limit-up", numarg( optarg ) );
237                      tr_bencDictAddInt( args, "speed-limit-up-enabled", 1 );
238                      break;
239            case 'U': tr_bencDictAddStr( &top, "method", "session-set" );
240                      tr_bencDictAddInt( args, "speed-limit-up-enabled", 0 );
241                      break;
242            case 'v': tr_bencDictAddStr( &top, "method", "torrent-verify" );
243                      addIdArg( args, id );
244                      break;
245            case 'w': tr_bencDictAddStr( &top, "method", "session-set" );
246                      tr_bencDictAddStr( args, "download-dir",
247                                         absolutify(buf,sizeof(buf),optarg) );
248                      break;
249            case 'x': tr_bencDictAddStr( &top, "method", "session-set" );
250                      tr_bencDictAddInt( args, "pex-allowed", 1 );
251                      break;
252            case 'X': tr_bencDictAddStr( &top, "method", "session-set" );
253                      tr_bencDictAddInt( args, "pex-allowed", 0 );
254                      break;
255            default:  fprintf( stderr, "got opt [%d]\n", (int)c );
256                      showUsage( );
257                      break;
258        }
259
260        if( addArg )
261            reqs[reqCount++] = tr_bencSaveAsJSON( &top, NULL );
262        tr_bencFree( &top );
263    }
264}
265
266/* [host:port] or [host] or [port] */
267static void
268getHostAndPort( int * argc, char ** argv, char ** host, int * port )
269{
270    if( *argv[1] != '-' )
271    {
272        int i;
273        const char * s = argv[1];
274        const char * delim = strchr( s, ':' );
275        if( delim ) { /* user passed in both host and port */
276            *host = tr_strndup( s, delim-s );
277            *port = atoi( delim+1 );
278        } else {
279            char * end;
280            const int i = strtol( s, &end, 10 );
281            if( !*end ) /* user passed in a port */
282                *port = i;
283            else /* user passed in a host */
284                *host = tr_strdup( s );
285        }
286
287        *argc -= 1;
288        for( i=1; i<*argc; ++i )
289            argv[i] = argv[i+1];
290    }
291}
292
293static size_t
294writeFunc( void * ptr, size_t size, size_t nmemb, void * buf )
295{
296    const size_t byteCount = size * nmemb;
297    evbuffer_add( buf, ptr, byteCount );
298    return byteCount;
299}
300
301static void
302etaToString( char * buf, size_t buflen, int64_t eta )
303{
304         if( eta < 0 )           snprintf( buf, buflen, "Unknown" );
305    else if( eta < 60 )          snprintf( buf, buflen, "%"PRId64"sec", eta );
306    else if( eta < (60*60) )     snprintf( buf, buflen, "%"PRId64" min", eta/60 );
307    else if( eta < (60*60*24) )  snprintf( buf, buflen, "%"PRId64" hrs", eta/(60*60) );
308    else                         snprintf( buf, buflen, "%"PRId64" days", eta/(60*60*24) );
309}
310
311#define KILOBYTE_FACTOR 1024.0
312#define MEGABYTE_FACTOR (1024.0 * 1024.0)
313#define GIGABYTE_FACTOR (1024.0 * 1024.0 * 1024.0)
314
315static char*
316strlratio( char * buf, double numerator, double denominator, size_t buflen )
317{
318    if( denominator )
319    {
320        const double ratio = numerator / denominator;
321        if( ratio < 10.0 )
322            snprintf( buf, buflen, "%'.2f", ratio );
323        else if( ratio < 100.0 )
324            snprintf( buf, buflen, "%'.1f", ratio );
325        else
326            snprintf( buf, buflen, "%'.0f", ratio );
327    }
328    else if( numerator )
329        tr_strlcpy( buf, "Infinity", buflen );
330    else
331        tr_strlcpy( buf, "None", buflen );
332    return buf;
333}
334
335static char*
336strlsize( char * buf, int64_t size, size_t buflen )
337{
338    if( !size )
339        tr_strlcpy( buf, "None", buflen );
340    else if( size < (int64_t)KILOBYTE_FACTOR )
341        snprintf( buf, buflen, "%'"PRId64" bytes", (int64_t)size );
342    else {
343        double displayed_size;
344        if (size < (int64_t)MEGABYTE_FACTOR) {
345            displayed_size = (double) size / KILOBYTE_FACTOR;
346            snprintf( buf, buflen, "%'.1f KB", displayed_size );
347        } else if (size < (int64_t)GIGABYTE_FACTOR) {
348            displayed_size = (double) size / MEGABYTE_FACTOR;
349            snprintf( buf, buflen, "%'.1f MB", displayed_size );
350        } else {
351            displayed_size = (double) size / GIGABYTE_FACTOR;
352            snprintf( buf, buflen, "%'.1f GB", displayed_size );
353        }
354    }
355    return buf;
356}
357
358static const char*
359torrentStatusToString( int i )
360{
361    switch( i )
362    {
363        case TR_STATUS_CHECK_WAIT: return "Will Verify";
364        case TR_STATUS_CHECK:      return "Verifying";
365        case TR_STATUS_DOWNLOAD:   return "Downloading";
366        case TR_STATUS_SEED:       return "Seeding";
367        case TR_STATUS_STOPPED:    return "Stopped";
368        default:                   return "Error";
369    }
370}
371
372static int
373isVerifying( int status )
374{
375    return ( ( status == TR_STATUS_CHECK_WAIT ) ||
376             ( status == TR_STATUS_CHECK ) );
377}
378
379static void
380printDetails( tr_benc * top )
381{
382    tr_benc *args, *torrents;
383
384    if( ( tr_bencDictFindDict( top, "arguments", &args ) ) &&
385        ( tr_bencDictFindList( args, "torrents", &torrents ) ) )
386    {
387        int ti, tCount;
388        for( ti=0, tCount=tr_bencListSize( torrents ); ti<tCount; ++ti )
389        {
390            tr_benc * t = tr_bencListChild( torrents, ti );
391            const char * str;
392            char buf[512];
393            char buf2[512];
394            int64_t i, j;
395
396            printf( "NAME\n" );
397            if( tr_bencDictFindInt( t, "id", &i ) )
398                printf( "  Id: %"PRId64"\n", i );
399            if( tr_bencDictFindStr( t, "name", &str ) )
400                printf( "  Name: %s\n", str );
401            if( tr_bencDictFindStr( t, "hashString", &str ) )
402                printf( "  Hash: %s\n", str );
403            printf( "\n" );
404
405            printf( "TRANSFER\n" );
406            if( tr_bencDictFindInt( t, "status", &i ) )
407            {
408                printf( "  State: %s\n", torrentStatusToString( i ) );
409
410                if( isVerifying( i ) &&
411                    tr_bencDictFindStr( t, "recheckProgress", &str ) )
412                        printf( "  Verified Progress: %.2f%%\n", atof(str) );
413            }
414
415            if( tr_bencDictFindInt( t, "sizeWhenDone", &i ) &&
416                tr_bencDictFindInt( t, "leftUntilDone", &j ) )
417            {
418                strlratio( buf, (i-j), i, sizeof( buf ) );
419                printf( "  Download Progress: %s%%\n", buf );
420            }
421
422            if( tr_bencDictFindInt( t, "eta", &i ) ) {
423                etaToString( buf, sizeof( buf ), i );
424                printf( "  ETA: %s\n", buf );
425            }
426            if( tr_bencDictFindInt( t, "rateDownload", &i ) )
427                printf( "  Download Speed: %.1f KB/s\n", i/1024.0 );
428            if( tr_bencDictFindInt( t, "rateUpload", &i ) )
429                printf( "  Upload Speed: %.1f KB/s\n", i/1024.0 );
430            if( tr_bencDictFindInt( t, "haveUnchecked", &i ) &&
431                tr_bencDictFindInt( t, "haveValid", &j ) )
432            {
433                strlsize( buf, i+j, sizeof( buf ) );
434                strlsize( buf2, j, sizeof( buf2 ) );
435                printf( "  Have: %s (%s verified)\n", buf, buf2 );
436            }
437
438            if( tr_bencDictFindInt( t, "sizeWhenDone", &i ) &&
439                tr_bencDictFindInt( t, "totalSize", &j ) )
440            {
441                strlsize( buf, j, sizeof( buf ) );
442                strlsize( buf2, i, sizeof( buf2 ) );
443                printf( "  Total size: %s (%s wanted)\n", buf, buf2 );
444            }
445            if( tr_bencDictFindInt( t, "downloadedEver", &i ) &&
446                tr_bencDictFindInt( t, "uploadedEver", &j ) ) {
447                strlsize( buf, i, sizeof( buf ) );
448                printf( "  Downloaded: %s\n", buf );
449                strlsize( buf, j, sizeof( buf ) );
450                printf( "  Uploaded: %s\n", buf );
451                strlratio( buf, i, j, sizeof( buf ) );
452                printf( "  Ratio: %s\n", buf );
453            }
454            if( tr_bencDictFindInt( t, "corruptEver", &i ) ) {
455                strlsize( buf, i, sizeof( buf ) );
456                printf( "  Corrupt DL: %s\n", buf );
457            }
458            if( tr_bencDictFindStr( t, "errorString", &str ) && str && *str )
459                printf( "  Error: %s\n", str );
460            printf( "\n" );
461           
462            printf( "HISTORY\n" );
463            if( tr_bencDictFindInt( t, "addedDate", &i ) && i ) {
464                const time_t tt = i;
465                printf( "  Date added:      %s", ctime( &tt ) );
466            }
467            if( tr_bencDictFindInt( t, "doneDate", &i ) && i ) {
468                const time_t tt = i;
469                printf( "  Date finished:   %s", ctime( &tt ) );
470            }
471            if( tr_bencDictFindInt( t, "startDate", &i ) && i ) {
472                const time_t tt = i;
473                printf( "  Date started:    %s", ctime( &tt ) );
474            }
475            if( tr_bencDictFindInt( t, "activityDate", &i ) && i ) {
476                const time_t tt = i;
477                printf( "  Latest activity: %s", ctime( &tt ) );
478            }
479            printf( "\n" );
480           
481            printf( "TRACKER\n" );
482            if( tr_bencDictFindInt( t, "lastAnnounceTime", &i ) && i ) {
483                const time_t tt = i;
484                printf( "  Latest announce: %s", ctime( &tt ) );
485            }
486            if( tr_bencDictFindStr( t, "announceURL", &str ) )
487                printf( "  Announce URL: %s\n", str );
488            if( tr_bencDictFindStr( t, "announceResponse", &str ) && str && *str )
489                printf( "  Announce response: %s\n", str );
490            if( tr_bencDictFindInt( t, "nextAnnounceTime", &i ) && i ) {
491                const time_t tt = i;
492                printf( "  Next announce:   %s", ctime( &tt ) );
493            }
494            if( tr_bencDictFindInt( t, "lastScrapeTime", &i ) && i ) {
495                const time_t tt = i;
496                printf( "  Latest scrape:   %s", ctime( &tt ) );
497            }
498            if( tr_bencDictFindStr( t, "scrapeResponse", &str ) )
499                printf( "  Scrape response: %s\n", str );
500            if( tr_bencDictFindInt( t, "nextScrapeTime", &i ) && i ) {
501                const time_t tt = i;
502                printf( "  Next scrape:     %s", ctime( &tt ) );
503            }
504            if( tr_bencDictFindInt( t, "seeders", &i ) &&
505                tr_bencDictFindInt( t, "leechers", &j ) )
506                printf( "  Tracker knows of %"PRId64" seeders and %"PRId64" leechers\n", i, j );
507            if( tr_bencDictFindInt( t, "timesCompleted", &i ) )
508                printf( "  Tracker has seen %"PRId64" clients complete this torrent\n", i );
509            printf( "\n" );
510
511            printf( "ORIGINS\n" );
512            if( tr_bencDictFindInt( t, "dateCreated", &i ) && i ) {
513                const time_t tt = i;
514                printf( "  Date created: %s", ctime( &tt ) );
515            }
516            if( tr_bencDictFindInt( t, "isPrivate", &i ) )
517                printf( "  Public torrent: %s\n", ( i ? "No" : "Yes" ) );
518            if( tr_bencDictFindStr( t, "comment", &str ) && str && *str )
519                printf( "  Comment: %s\n", str );
520            if( tr_bencDictFindStr( t, "creator", &str ) && str && *str )
521                printf( "  Creator: %s\n", str );
522            if( tr_bencDictFindInt( t, "pieceCount", &i ) )
523                printf( "  Piece Count: %"PRId64"\n", i );
524            if( tr_bencDictFindInt( t, "pieceSize", &i ) )
525                printf( "  Piece Size: %"PRId64"\n", i );
526        }
527    }
528}
529
530static void
531printFileList( tr_benc * top )
532{
533    tr_benc *args, *torrents;
534
535    if( ( tr_bencDictFindDict( top, "arguments", &args ) ) &&
536        ( tr_bencDictFindList( args, "torrents", &torrents ) ) )
537    {
538        int i, in;
539        for( i=0, in=tr_bencListSize( torrents ); i<in; ++i )
540        {
541            tr_benc * d = tr_bencListChild( torrents, i );
542            tr_benc *files, *priorities, *wanteds;
543            const char * name;
544            if( tr_bencDictFindStr( d, "name", &name ) &&
545                tr_bencDictFindList( d, "files", &files ) &&
546                tr_bencDictFindList( d, "priorities", &priorities ) &&
547                tr_bencDictFindList( d, "wanted", &wanteds ) )
548            {
549                int j=0, jn=tr_bencListSize(files);
550                printf( "%s (%d files):\n", name, jn );
551                printf("%3s  %8s %3s %9s  %s\n", "#", "Priority", "Get", "Size", "Name" );
552                for( j=0, jn=tr_bencListSize( files ); j<jn; ++j )
553                {
554                    int64_t length;
555                    int64_t priority;
556                    int64_t wanted;
557                    const char * filename;
558                    tr_benc * file = tr_bencListChild( files, j );
559                    if( tr_bencDictFindInt( file, "length", &length ) &&
560                        tr_bencDictFindStr( file, "name", &filename ) &&
561                        tr_bencGetInt( tr_bencListChild( priorities, j ), &priority ) &&
562                        tr_bencGetInt( tr_bencListChild( wanteds, j ), &wanted ) )
563                    {
564                        char sizestr[64];
565                        strlsize( sizestr, length, sizeof( sizestr ) );
566                        const char * pristr;
567                        switch( priority ) {
568                            case TR_PRI_LOW:    pristr = "Low"; break;
569                            case TR_PRI_HIGH:   pristr = "High"; break;
570                            default:            pristr = "Normal"; break;
571                        }
572                        printf( "%3d: %-8s %-3s %9s  %s\n", (j+1), pristr, (wanted?"Yes":"No"), sizestr, filename );
573                    }
574                }
575            }
576        }
577    }
578}
579
580static void
581printTorrentList( tr_benc * top )
582{
583    tr_benc *args, *list;
584
585    if( ( tr_bencDictFindDict( top, "arguments", &args ) ) &&
586        ( tr_bencDictFindList( args, "torrents", &list ) ) )
587    {
588        int i, n;
589        printf( "%-3s  %-4s  %-8s  %-5s  %-5s  %-5s  %-11s  %s\n",
590                "ID", "Done", "ETA", "Up", "Down", "Ratio", "Status", "Name" );
591        for( i=0, n=tr_bencListSize( list ); i<n; ++i )
592        {
593            int64_t id, eta, status, up, down;
594            int64_t sizeWhenDone, leftUntilDone;
595            int64_t upEver, downEver;
596            const char *name;
597            tr_benc * d = tr_bencListChild( list, i );
598            if(    tr_bencDictFindInt( d, "downloadedEver", &downEver )
599                && tr_bencDictFindInt( d, "eta", &eta )
600                && tr_bencDictFindInt( d, "id", &id )
601                && tr_bencDictFindInt( d, "leftUntilDone", &leftUntilDone )
602                && tr_bencDictFindStr( d, "name", &name )
603                && tr_bencDictFindInt( d, "rateDownload", &down )
604                && tr_bencDictFindInt( d, "rateUpload", &up )
605                && tr_bencDictFindInt( d, "sizeWhenDone", &sizeWhenDone )
606                && tr_bencDictFindInt( d, "status", &status )
607                && tr_bencDictFindInt( d, "uploadedEver", &upEver ) )
608            {
609                char etaStr[16];
610                if( leftUntilDone )
611                    etaToString( etaStr, sizeof( etaStr ), eta );
612                else
613                    snprintf( etaStr, sizeof( etaStr ), "Done" );
614                printf( "%3d  %3d%%  %-8s  %5.1f  %5.1f  %5.1f  %-11s  %s\n",
615                        (int)id,
616                        (int)(100.0*(sizeWhenDone-leftUntilDone)/sizeWhenDone),
617                        etaStr,
618                        up / 1024.0,
619                        down / 1024.0,
620                        (double)(downEver ? ((double)upEver/downEver) : 0.0),
621                        torrentStatusToString( status ),
622                        name );
623            }
624        }
625    }
626}
627
628static void
629processResponse( const char * host, int port,
630                 const void * response, size_t len )
631{
632    tr_benc top;
633
634    if( debug )
635        fprintf( stderr, "got response: [%*.*s]\n",
636                 (int)len, (int)len, (const char*) response );
637
638    if( tr_jsonParse( response, len, &top, NULL ) )
639       tr_nerr( MY_NAME, "Unable to parse response \"%*.*s\"", (int)len, (int)len, (char*)response );
640    else
641    {
642        int64_t tag = -1;
643        const char * str;
644        tr_bencDictFindInt( &top, "tag", &tag );
645
646        if( tr_bencDictFindStr( &top, "result", &str ) )
647            printf( "%s:%d responded: \"%s\"\n", host, port, str );
648        switch( tag ) {
649            case TAG_FILES: printFileList( &top ); break;
650            case TAG_DETAILS: printDetails( &top ); break;
651            case TAG_LIST: printTorrentList( &top ); break;
652            default: break;
653        }
654
655        tr_bencFree( &top );
656    }
657}
658
659static void
660processRequests( const char * host, int port,
661                 const char ** reqs, int reqCount )
662{
663    int i;
664    CURL * curl;
665    struct evbuffer * buf = evbuffer_new( );
666    char * url = tr_strdup_printf( "http://%s:%d/transmission/rpc", host, port );
667
668    curl = curl_easy_init( );
669    curl_easy_setopt( curl, CURLOPT_VERBOSE, debug );
670    curl_easy_setopt( curl, CURLOPT_USERAGENT, MY_NAME"/"LONG_VERSION_STRING );
671    curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, writeFunc );
672    curl_easy_setopt( curl, CURLOPT_WRITEDATA, buf );
673    curl_easy_setopt( curl, CURLOPT_POST, 1 );
674    curl_easy_setopt( curl, CURLOPT_URL, url );
675    if( auth ) {
676        curl_easy_setopt( curl, CURLOPT_USERPWD, auth );
677        curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
678    }
679
680    for( i=0; i<reqCount; ++i )
681    {
682        CURLcode res;
683        curl_easy_setopt( curl, CURLOPT_POSTFIELDS, reqs[i] );
684        if( debug )
685            tr_ninf( MY_NAME, "posting [%s]\n", reqs[i] );
686        if(( res = curl_easy_perform( curl )))
687            tr_nerr( MY_NAME, "(%s:%d) %s", host, port, curl_easy_strerror( res ) );
688        else
689            processResponse( host, port, EVBUFFER_DATA( buf ), EVBUFFER_LENGTH( buf ) );
690
691        evbuffer_drain( buf, EVBUFFER_LENGTH( buf ) );
692    }
693
694    /* cleanup */
695    tr_free( url );
696    evbuffer_free( buf );
697    curl_easy_cleanup( curl );
698}
699
700int
701main( int argc, char ** argv )
702{
703    int i;
704    int port = DEFAULT_PORT;
705    char * host = NULL;
706
707    if( argc < 2 )
708        showUsage( );
709
710    getHostAndPort( &argc, argv, &host, &port );
711    if( host == NULL )
712        host = tr_strdup( DEFAULT_HOST );
713
714    readargs( argc, (const char**)argv );
715    if( reqCount )
716        processRequests( host, port, (const char**)reqs, reqCount );
717    else
718        showUsage( );
719
720    for( i=0; i<reqCount; ++i )
721        tr_free( reqs[i] );
722
723    tr_free( host );
724    return 0;
725}
Note: See TracBrowser for help on using the repository browser.