source: trunk/daemon/remote.c @ 6295

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

cleanup the new getopt code a bit. sync remote's manpage.

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