source: trunk/daemon/remote.c @ 6294

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

(daemon) allow wildcards to be used when adding torrents again, such as "transmission-remote -a ~/Desktop/*torrent"

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