Changeset 10543


Ignore:
Timestamp:
Apr 29, 2010, 3:01:39 AM (11 years ago)
Author:
charles
Message:

(trunk daemon) #2921 "adding (-a) torrents should set them as the current torrents" -- implemented for 2.00

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/daemon/remote.c

    r10536 r10543  
    1111 */
    1212
     13#include <assert.h>
    1314#include <ctype.h> /* isspace */
    1415#include <errno.h>
     
    4142#define DEFAULT_PORT atoi(TR_DEFAULT_RPC_PORT_STR)
    4243
    43 enum { TAG_SESSION, TAG_STATS, TAG_LIST, TAG_DETAILS, TAG_FILES, TAG_PEERS, TAG_PORTTEST };
     44#define ARGUMENTS "arguments"
     45
     46/***
     47****
     48****  Display Utilities
     49****
     50***/
     51
     52static void
     53etaToString( char *  buf, size_t  buflen, int64_t eta )
     54{
     55    if( eta < 0 )
     56        tr_snprintf( buf, buflen, "Unknown" );
     57    else if( eta < 60 )
     58        tr_snprintf( buf, buflen, "%" PRId64 "sec", eta );
     59    else if( eta < ( 60 * 60 ) )
     60        tr_snprintf( buf, buflen, "%" PRId64 " min", eta / 60 );
     61    else if( eta < ( 60 * 60 * 24 ) )
     62        tr_snprintf( buf, buflen, "%" PRId64 " hrs", eta / ( 60 * 60 ) );
     63    else
     64        tr_snprintf( buf, buflen, "%" PRId64 " days", eta / ( 60 * 60 * 24 ) );
     65}
     66
     67static char*
     68tr_strltime( char * buf, int seconds, size_t buflen )
     69{
     70    int  days, hours, minutes;
     71    char d[128], h[128], m[128], s[128];
     72
     73    if( seconds < 0 )
     74        seconds = 0;
     75
     76    days = seconds / 86400;
     77    hours = ( seconds % 86400 ) / 3600;
     78    minutes = ( seconds % 3600 ) / 60;
     79    seconds = ( seconds % 3600 ) % 60;
     80
     81    tr_snprintf( d, sizeof( d ), "%'d day%s", days, days==1?"":"s" );
     82    tr_snprintf( h, sizeof( h ), "%'d hour%s", hours, hours==1?"":"s" );
     83    tr_snprintf( m, sizeof( m ), "%'d minute%s", minutes, minutes==1?"":"s" );
     84    tr_snprintf( s, sizeof( s ), "%'d second%s", seconds, seconds==1?"":"s" );
     85
     86    if( days )
     87    {
     88        if( days >= 4 || !hours )
     89            tr_strlcpy( buf, d, buflen );
     90        else
     91            tr_snprintf( buf, buflen, "%s, %s", d, h );
     92    }
     93    else if( hours )
     94    {
     95        if( hours >= 4 || !minutes )
     96            tr_strlcpy( buf, h, buflen );
     97        else
     98            tr_snprintf( buf, buflen, "%s, %s", h, m );
     99    }
     100    else if( minutes )
     101    {
     102        if( minutes >= 4 || !seconds )
     103            tr_strlcpy( buf, m, buflen );
     104        else
     105            tr_snprintf( buf, buflen, "%s, %s", m, s );
     106    }
     107    else tr_strlcpy( buf, s, buflen );
     108
     109    return buf;
     110}
     111
     112static const double KiB = 1024.0;
     113static const double MiB = 1024.0 * 1024.0;
     114static const double GiB = 1024.0 * 1024.0 * 1024.0;
     115
     116static char*
     117strlratio2( char * buf, double ratio, size_t buflen )
     118{
     119    return tr_strratio( buf, buflen, ratio, "Inf" );
     120}
     121
     122static char*
     123strlratio( char * buf, double numerator, double denominator, size_t buflen )
     124{
     125    double ratio;
     126
     127    if( denominator )
     128        ratio = numerator / denominator;
     129    else if( numerator )
     130        ratio = TR_RATIO_INF;
     131    else
     132        ratio = TR_RATIO_NA;
     133
     134    return strlratio2( buf, ratio, buflen );
     135}
     136
     137static char*
     138strlsize( char *  buf, int64_t size, size_t  buflen )
     139{
     140    if( !size )
     141        tr_strlcpy( buf, "None", buflen );
     142    else if( size < (int64_t)KiB )
     143        tr_snprintf( buf, buflen, "%'" PRId64 " bytes", (int64_t)size );
     144    else
     145    {
     146        double displayed_size;
     147        if( size < (int64_t)MiB )
     148        {
     149            displayed_size = (double) size / KiB;
     150            tr_snprintf( buf, buflen, "%'.1f KiB", displayed_size );
     151        }
     152        else if( size < (int64_t)GiB )
     153        {
     154            displayed_size = (double) size / MiB;
     155            tr_snprintf( buf, buflen, "%'.1f MiB", displayed_size );
     156        }
     157        else
     158        {
     159            displayed_size = (double) size / GiB;
     160            tr_snprintf( buf, buflen, "%'.1f GiB", displayed_size );
     161        }
     162    }
     163    return buf;
     164}
     165
     166enum { TAG_SESSION, TAG_STATS, TAG_LIST, TAG_DETAILS, TAG_FILES, TAG_PEERS, TAG_PORTTEST, TAG_TORRENT_ADD };
    44167
    45168static const char*
     
    59182                "\n"
    60183                "See the man page for detailed explanations and many examples.";
     184}
     185
     186enum
     187{
     188    MODE_TORRENT_START         = (1<<0),
     189    MODE_TORRENT_STOP          = (1<<1),
     190    MODE_TORRENT_VERIFY        = (1<<2),
     191    MODE_TORRENT_REANNOUNCE    = (1<<3),
     192    MODE_TORRENT_SET           = (1<<4),
     193    MODE_TORRENT_GET           = (1<<5),
     194    MODE_TORRENT_ADD           = (1<<6),
     195    MODE_TORRENT_REMOVE        = (1<<7),
     196    MODE_TORRENT_SET_LOCATION  = (1<<8),
     197    MODE_SESSION_SET           = (1<<9),
     198    MODE_SESSION_GET           = (1<<10),
     199    MODE_SESSION_STATS         = (1<<11),
     200    MODE_BLOCKLIST_UPDATE      = (1<<12),
     201    MODE_PORT_TEST             = (1<<13)
     202};
     203
     204static int
     205getOptMode( int val )
     206{
     207    switch( val )
     208    {
     209        case TR_OPT_ERR:
     210        case TR_OPT_UNK:
     211        case 'a': /* add torrent */
     212        case 'b': /* debug */
     213        case 'n': /* auth */
     214        case 'N': /* netrc */
     215        case 't': /* set current torrent */
     216        case 'V': /* show version number */
     217            return 0;
     218
     219        case 'c': /* incomplete-dir */
     220        case 'C': /* no-incomplete-dir */
     221        case 'm': /* portmap */
     222        case 'M': /* "no-portmap */
     223        case 'o': /* dht */
     224        case 'O': /* no-dht */
     225        case 'p': /* incoming peer port */
     226        case 'P': /* random incoming peer port */
     227        case 'x': /* pex */
     228        case 'X': /* no-pex */
     229        case 970: /* alt-speed */
     230        case 971: /* no-alt-speed */
     231        case 972: /* alt-speed-downlimit */
     232        case 973: /* alt-speed-uplimit */
     233        case 974: /* alt-speed-scheduler */
     234        case 975: /* no-alt-speed-scheduler */
     235        case 976: /* alt-speed-time-begin */
     236        case 977: /* alt-speed-time-end */
     237        case 978: /* alt-speed-days */
     238        case 910: /* encryption-required */
     239        case 911: /* encryption-preferred */
     240        case 912: /* encryption-tolerated */
     241        case 931: /* global-peers */
     242        case 953: /* global-seedratio */
     243        case 954: /* no-global-seedratio */
     244        case 990: /* start-paused */
     245        case 991: /* no-start-paused */
     246        case 992: /* trash-torrent */
     247        case 993: /* no-trash-torrent */
     248            return MODE_SESSION_SET;
     249
     250        case 930: /* peers */
     251        case 950: /* seedratio */
     252        case 951: /* seedratio-default */
     253        case 952: /* no-seedratio */
     254        case 984: /* honor-session */
     255        case 985: /* no-honor-session */
     256            return MODE_TORRENT_SET;
     257
     258        case 920: /* session-info */
     259            return MODE_SESSION_GET;
     260
     261        case 'g': /* get */
     262        case 'G': /* no-get */
     263        case 900: /* priority-high */
     264        case 901: /* priority-normal */
     265        case 902: /* priority-low */
     266            return MODE_TORRENT_SET | MODE_TORRENT_ADD;
     267
     268        case 961: /* find */
     269            return MODE_TORRENT_SET_LOCATION | MODE_TORRENT_ADD;
     270
     271        case 'f': /* files */
     272        case 'i': /* info */
     273        case 'l': /* list all torrents */
     274        case 940: /* peer-info */
     275            return MODE_TORRENT_GET;
     276
     277        case 'd': /* download speed limit */
     278        case 'D': /* no download speed limit */
     279        case 'u': /* upload speed limit */
     280        case 'U': /* no upload speed limit */
     281            return MODE_SESSION_SET | MODE_TORRENT_SET;
     282
     283        case 's': /* start */
     284            return MODE_TORRENT_START | MODE_TORRENT_ADD;
     285
     286        case 'S': /* stop */
     287            return MODE_TORRENT_STOP | MODE_TORRENT_ADD;
     288
     289        case 'w': /* download-dir */
     290            return MODE_SESSION_SET | MODE_TORRENT_ADD;
     291
     292        case 963: /* blocklist-update */
     293            return MODE_BLOCKLIST_UPDATE;
     294
     295        case 921: /* session-stats */
     296            return MODE_SESSION_STATS;
     297
     298        case 'v': /* verify */
     299            return MODE_TORRENT_VERIFY;
     300
     301        case 962: /* port-test */
     302            return MODE_PORT_TEST;
     303
     304        case 'r': /* remove */
     305        case 'R': /* remove and delete */
     306            return MODE_TORRENT_REMOVE;
     307
     308        case 960: /* move */
     309            return MODE_TORRENT_SET_LOCATION;
     310
     311        default:
     312            fprintf( stderr, "unrecognized argument %d\n", val );
     313            assert( "unrecognized argument" && 0 );
     314            return 0;
     315    }
    61316}
    62317
     
    119374    { 992, "trash-torrent",         "Delete torrents after adding", NULL, 0, NULL },
    120375    { 993, "no-trash-torrent",      "Do not delete torrents after adding", NULL, 0, NULL },
    121     { 980, "torrent-downlimit",     "Set the maximum download speed for the current torrent(s) in KiB/s", "td",  1, "<speed>" },
    122     { 981, "no-torrent-downlimit",  "Don't limit the download speed for the current torrent(s)", "TD",  0, NULL },
    123376    { 982, "torrent-uplimit",       "Set the maximum upload speed for the current torrent(s) in KiB/s", "tu",  1, "<speed>" },
    124377    { 983, "no-torrent-uplimit",    "Don't limit the upload speed for the current torrent(s)", "TU",  0, NULL },
     
    157410}
    158411
    159 static char * reqs[256]; /* arbitrary max */
    160 static int    reqCount = 0;
    161412static tr_bool debug = 0;
    162413static char * auth = NULL;
     
    209460
    210461static void
    211 addIdArg( tr_benc *    args,
    212           const char * id )
     462addIdArg( tr_benc * args, const char * id )
    213463{
    214464    if( !*id )
     
    384634};
    385635
    386 static int
    387 readargs( int argc, const char ** argv )
    388 {
    389     int c;
    390     tr_bool addingTorrents = FALSE;
    391     int status = EXIT_SUCCESS;
    392     char id[4096];
    393     const char * optarg;
    394 
    395     *id = '\0';
    396 
    397     while( ( c = tr_getopt( getUsage( ), argc, argv, opts, &optarg ) ) )
    398     {
    399         int     i, n;
    400         int     addArg = TRUE;
    401         tr_benc top, *args, *fields;
    402         tr_bencInitDict( &top, 3 );
    403         args = tr_bencDictAddDict( &top, "arguments", 0 );
    404 
    405         switch( c )
    406         {
    407             case TR_OPT_UNK:
    408                 if( addingTorrents )
    409                 {
    410                     char * tmp = getEncodedMetainfo( optarg );
    411                     if( tmp )
    412                     {
    413                         tr_bencDictAddStr( &top, "method", "torrent-add" );
    414                         tr_bencDictAddStr( args, "metainfo", tmp );
    415                         tr_free( tmp );
    416                     }
    417                     else
    418                     {
    419                         tr_bencDictAddStr( &top, "method", "torrent-add" );
    420                         tr_bencDictAddStr( args, "filename", optarg );
    421                     }
    422                 }
    423                 else
    424                 {
    425                     fprintf( stderr, "Unknown option: %s\n", optarg );
    426                     addArg = FALSE;
    427                     status |= EXIT_FAILURE;
    428                 }
    429                 break;
    430 
    431             case 'a':
    432                 addingTorrents = TRUE;
    433                 addArg = FALSE;
    434                 break;
    435 
    436             case 'b':
    437                 debug = TRUE;
    438                 addArg = FALSE;
    439                 break;
    440 
    441             case 'c':
    442                 tr_bencDictAddStr( &top, "method", "session-set" );
    443                 tr_bencDictAddStr( args, TR_PREFS_KEY_INCOMPLETE_DIR, optarg );
    444                 tr_bencDictAddBool( args, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, TRUE );
    445                 break;
    446 
    447             case 'C':
    448                 tr_bencDictAddStr( &top, "method", "session-set" );
    449                 tr_bencDictAddBool( args, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, FALSE );
    450                 break;
    451 
    452             case 'd':
    453                 tr_bencDictAddStr( &top, "method", "session-set" );
    454                 tr_bencDictAddInt( args, TR_PREFS_KEY_DSPEED, numarg( optarg ) );
    455                 tr_bencDictAddBool( args, TR_PREFS_KEY_DSPEED_ENABLED, TRUE );
    456                 break;
    457 
    458             case 'D':
    459                 tr_bencDictAddStr( &top, "method", "session-set" );
    460                 tr_bencDictAddBool( args, TR_PREFS_KEY_DSPEED_ENABLED, FALSE );
    461                 break;
    462 
    463             case 'f':
    464                 tr_bencDictAddStr( &top, "method", "torrent-get" );
    465                 tr_bencDictAddInt( &top, "tag", TAG_FILES );
    466                 addIdArg( args, id );
    467                 n = TR_N_ELEMENTS( files_keys );
    468                 fields = tr_bencDictAddList( args, "fields", n );
    469                 for( i = 0; i < n; ++i )
    470                     tr_bencListAddStr( fields, files_keys[i] );
    471                 break;
    472 
    473             case 'g':
    474                 tr_bencDictAddStr( &top, "method", "torrent-set" );
    475                 addIdArg( args, id );
    476                 addFiles( args, "files-wanted", optarg );
    477                 break;
    478 
    479             case 'G':
    480                 tr_bencDictAddStr( &top, "method", "torrent-set" );
    481                 addIdArg( args, id );
    482                 addFiles( args, "files-unwanted", optarg );
    483                 break;
    484 
    485             case 'i':
    486                 tr_bencDictAddStr( &top, "method", "torrent-get" );
    487                 tr_bencDictAddInt( &top, "tag", TAG_DETAILS );
    488                 addIdArg( args, id );
    489                 n = TR_N_ELEMENTS( details_keys );
    490                 fields = tr_bencDictAddList( args, "fields", n );
    491                 for( i = 0; i < n; ++i )
    492                     tr_bencListAddStr( fields, details_keys[i] );
    493                 break;
    494 
    495             case 'l':
    496                 tr_bencDictAddStr( &top, "method", "torrent-get" );
    497                 tr_bencDictAddInt( &top, "tag", TAG_LIST );
    498                 n = TR_N_ELEMENTS( list_keys );
    499                 fields = tr_bencDictAddList( args, "fields", n );
    500                 for( i = 0; i < n; ++i )
    501                     tr_bencListAddStr( fields, list_keys[i] );
    502                 break;
    503 
    504             case 'm':
    505                 tr_bencDictAddStr( &top, "method", "session-set" );
    506                 tr_bencDictAddBool( args, TR_PREFS_KEY_PORT_FORWARDING, TRUE );
    507                 break;
    508 
    509             case 'M':
    510                 tr_bencDictAddStr( &top, "method", "session-set" );
    511                 tr_bencDictAddBool( args, TR_PREFS_KEY_PORT_FORWARDING, FALSE );
    512                 break;
    513 
    514             case 'n':
    515                 auth = tr_strdup( optarg );
    516                 addArg = FALSE;
    517                 break;
    518 
    519             case 'N':
    520                 netrc = tr_strdup( optarg );
    521                 addArg = FALSE;
    522                 break;
    523 
    524             case 'p':
    525                 tr_bencDictAddStr( &top, "method", "session-set" );
    526                 tr_bencDictAddInt( args, TR_PREFS_KEY_PEER_PORT, numarg( optarg ) );
    527                 break;
    528 
    529             case 'P':
    530                 tr_bencDictAddStr( &top, "method", "session-set" );
    531                 tr_bencDictAddBool( args, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, TRUE);
    532                 break;
    533 
    534             case 'r':
    535                 tr_bencDictAddStr( &top, "method", "torrent-remove" );
    536                 addIdArg( args, id );
    537                 break;
    538 
    539             case 'R':
    540                 tr_bencDictAddStr( &top, "method", "torrent-remove" );
    541                 addIdArg( args, id );
    542                 tr_bencDictAddBool( args, "delete-local-data", TRUE );
    543                 break;
    544 
    545             case 's':
    546                 tr_bencDictAddStr( &top, "method", "torrent-start" );
    547                 addIdArg( args, id );
    548                 break;
    549 
    550             case 'S':
    551                 tr_bencDictAddStr( &top, "method", "torrent-stop" );
    552                 addIdArg( args, id );
    553                 break;
    554 
    555             case 't':
    556                 tr_strlcpy( id, optarg, sizeof( id ) );
    557                 addArg = FALSE;
    558                 break;
    559 
    560             case 'u':
    561                 tr_bencDictAddStr( &top, "method", "session-set" );
    562                 tr_bencDictAddInt( args, TR_PREFS_KEY_USPEED, numarg( optarg ) );
    563                 tr_bencDictAddBool( args, TR_PREFS_KEY_USPEED_ENABLED, TRUE );
    564                 break;
    565 
    566             case 'U':
    567                 tr_bencDictAddStr( &top, "method", "session-set" );
    568                 tr_bencDictAddBool( args, TR_PREFS_KEY_USPEED_ENABLED, FALSE );
    569                 break;
    570 
    571             case 'v':
    572                 tr_bencDictAddStr( &top, "method", "torrent-verify" );
    573                 addIdArg( args, id );
    574                 break;
    575 
    576             case 'V':
    577                 fprintf( stderr, "Transmission %s\n", LONG_VERSION_STRING );
    578                 exit( 0 );
    579                 break;
    580 
    581             case 'w': {
    582                 char * path = absolutify( optarg );
    583                 tr_bencDictAddStr( &top, "method", "session-set" );
    584                 tr_bencDictAddStr( args, TR_PREFS_KEY_DOWNLOAD_DIR, path );
    585                 tr_free( path );
    586                 break;
    587             }
    588 
    589             case 'o':
    590                 tr_bencDictAddStr( &top, "method", "session-set" );
    591                 tr_bencDictAddBool( args, TR_PREFS_KEY_DHT_ENABLED, TRUE );
    592                 break;
    593 
    594             case 'O':
    595                 tr_bencDictAddStr( &top, "method", "session-set" );
    596                 tr_bencDictAddBool( args, TR_PREFS_KEY_DHT_ENABLED, FALSE );
    597                 break;
    598 
    599             case 'x':
    600                 tr_bencDictAddStr( &top, "method", "session-set" );
    601                 tr_bencDictAddBool( args, TR_PREFS_KEY_PEX_ENABLED, TRUE );
    602                 break;
    603 
    604             case 'X':
    605                 tr_bencDictAddStr( &top, "method", "session-set" );
    606                 tr_bencDictAddBool( args, TR_PREFS_KEY_PEX_ENABLED, FALSE );
    607                 break;
    608 
    609             case 900:
    610                 tr_bencDictAddStr( &top, "method", "torrent-set" );
    611                 addIdArg( args, id );
    612                 addFiles( args, "priority-high", optarg );
    613                 break;
    614 
    615             case 901:
    616                 tr_bencDictAddStr( &top, "method", "torrent-set" );
    617                 addIdArg( args, id );
    618                 addFiles( args, "priority-normal", optarg );
    619                 break;
    620 
    621             case 902:
    622                 tr_bencDictAddStr( &top, "method", "torrent-set" );
    623                 addIdArg( args, id );
    624                 addFiles( args, "priority-low", optarg );
    625                 break;
    626 
    627             case 910:
    628                 tr_bencDictAddStr( &top, "method", "session-set" );
    629                 tr_bencDictAddStr( args, TR_PREFS_KEY_ENCRYPTION, "required" );
    630                 break;
    631 
    632             case 911:
    633                 tr_bencDictAddStr( &top, "method", "session-set" );
    634                 tr_bencDictAddStr( args, TR_PREFS_KEY_ENCRYPTION, "preferred" );
    635                 break;
    636 
    637             case 912:
    638                 tr_bencDictAddStr( &top, "method", "session-set" );
    639                 tr_bencDictAddStr( args, TR_PREFS_KEY_ENCRYPTION, "tolerated" );
    640                 break;
    641 
    642             case 920:
    643                 tr_bencDictAddStr( &top, "method", "session-get" );
    644                 tr_bencDictAddInt( &top, "tag", TAG_SESSION );
    645                 break;
    646 
    647             case 921:
    648                 tr_bencDictAddStr( &top, "method", "session-stats" );
    649                 tr_bencDictAddInt( &top, "tag", TAG_STATS );
    650                 break;
    651 
    652             case 930:
    653                 tr_bencDictAddStr( &top, "method", "torrent-set" );
    654                 addIdArg( args, id );
    655                 tr_bencDictAddInt( args, "peer-limit", atoi(optarg) );
    656                 break;
    657 
    658             case 931:
    659                 tr_bencDictAddStr( &top, "method", "session-set" );
    660                 tr_bencDictAddInt( args, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, atoi(optarg) );
    661                 break;
    662 
    663             case 940:
    664                 tr_bencDictAddStr( &top, "method", "torrent-get" );
    665                 tr_bencDictAddInt( &top, "tag", TAG_PEERS );
    666                 addIdArg( args, id );
    667                 fields = tr_bencDictAddList( args, "fields", 1 );
    668                 tr_bencListAddStr( fields, "peers" );
    669                 break;
    670 
    671             case 950:
    672                 tr_bencDictAddStr( &top, "method", "torrent-set" );
    673                 tr_bencDictAddReal( args, "seedRatioLimit", atof(optarg) );
    674                 tr_bencDictAddInt( args, "seedRatioMode", TR_RATIOLIMIT_SINGLE );
    675                 addIdArg( args, id );
    676                 break;
    677 
    678             case 951:
    679                 tr_bencDictAddStr( &top, "method", "torrent-set" );
    680                 tr_bencDictAddInt( args, "seedRatioMode", TR_RATIOLIMIT_GLOBAL );
    681                 addIdArg( args, id );
    682                 break;
    683 
    684             case 952:
    685                 tr_bencDictAddStr( &top, "method", "torrent-set" );
    686                 tr_bencDictAddInt( args, "seedRatioMode", TR_RATIOLIMIT_UNLIMITED );
    687                 addIdArg( args, id );
    688                 break;
    689 
    690             case 953:
    691                 tr_bencDictAddStr( &top, "method", "session-set" );
    692                 tr_bencDictAddReal( args, "seedRatioLimit", atof(optarg) );
    693                 tr_bencDictAddBool( args, "seedRatioLimited", TRUE );
    694                 break;
    695 
    696             case 954:
    697                 tr_bencDictAddStr( &top, "method", "session-set" );
    698                 tr_bencDictAddBool( args, "seedRatioLimited", FALSE );
    699                 break;
    700 
    701             case 960:
    702                 tr_bencDictAddStr( &top, "method", "torrent-set-location" );
    703                 tr_bencDictAddStr( args, "location", optarg );
    704                 tr_bencDictAddBool( args, "move", TRUE );
    705                 addIdArg( args, id );
    706                 break;
    707 
    708             case 961:
    709                 tr_bencDictAddStr( &top, "method", "torrent-set-location" );
    710                 tr_bencDictAddStr( args, "location", optarg );
    711                 tr_bencDictAddBool( args, "move", FALSE );
    712                 addIdArg( args, id );
    713                 break;
    714 
    715             case 962:
    716                 tr_bencDictAddStr( &top, "method", "port-test" );
    717                 tr_bencDictAddInt( &top, "tag", TAG_PORTTEST );
    718                 break;
    719 
    720             case 963:
    721                 tr_bencDictAddStr( &top, "method", "blocklist-update" );
    722                 break;
    723 
    724             case 970:
    725                 tr_bencDictAddStr( &top, "method", "session-set" );
    726                 tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_ENABLED, TRUE );
    727                 break;
    728 
    729             case 971:
    730                 tr_bencDictAddStr( &top, "method", "session-set" );
    731                 tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_ENABLED, FALSE );
    732                 break;
    733 
    734             case 972:
    735                 tr_bencDictAddStr( &top, "method", "session-set" );
    736                 tr_bencDictAddInt( args, TR_PREFS_KEY_ALT_SPEED_DOWN, numarg( optarg ) );
    737                 break;
    738 
    739             case 973:
    740                 tr_bencDictAddStr( &top, "method", "session-set" );
    741                 tr_bencDictAddInt( args, TR_PREFS_KEY_ALT_SPEED_UP, numarg( optarg ) );
    742                 break;
    743 
    744             case 974:
    745                 tr_bencDictAddStr( &top, "method", "session-set" );
    746                 tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, TRUE );
    747                 break;
    748 
    749             case 975:
    750                 tr_bencDictAddStr( &top, "method", "session-set" );
    751                 tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, FALSE );
    752                 break;
    753 
    754             case 976:
    755                 tr_bencDictAddStr( &top, "method", "session-set" );
    756                 addTime( args, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, optarg);
    757                 break;
    758 
    759             case 977:
    760                 tr_bencDictAddStr( &top, "method", "session-set" );
    761                 addTime( args, TR_PREFS_KEY_ALT_SPEED_TIME_END, optarg);
    762                 break;
    763 
    764             case 978:
    765                 tr_bencDictAddStr( &top, "method", "session-set" );
    766                 addDays( args, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, optarg );
    767                 break;
    768 
    769             case 980:
    770                 tr_bencDictAddStr( &top, "method", "torrent-set" );
    771                 addIdArg( args, id );
    772                 tr_bencDictAddInt( args, "downloadLimit", numarg( optarg ) );
    773                 tr_bencDictAddBool( args, "downloadLimited", TRUE );
    774                 break;
    775 
    776             case 981:
    777                 tr_bencDictAddStr( &top, "method", "torrent-set" );
    778                 addIdArg( args, id );
    779                 tr_bencDictAddBool( args, "downloadLimited", FALSE );
    780                 break;
    781 
    782             case 982:
    783                 tr_bencDictAddStr( &top, "method", "torrent-set" );
    784                 addIdArg( args, id );
    785                 tr_bencDictAddInt( args, "uploadLimit", numarg( optarg ) );
    786                 tr_bencDictAddBool( args, "uploadLimited", TRUE );
    787                 break;
    788 
    789             case 983:
    790                 tr_bencDictAddStr( &top, "method", "torrent-set" );
    791                 addIdArg( args, id );
    792                 tr_bencDictAddBool( args, "uploadLimited", FALSE );
    793                 break;
    794 
    795             case 984:
    796                 tr_bencDictAddStr( &top, "method", "torrent-set" );
    797                 addIdArg( args, id );
    798                 tr_bencDictAddBool( args, "honorsSessionLimits", TRUE );
    799                 break;
    800 
    801             case 985:
    802                 tr_bencDictAddStr( &top, "method", "torrent-set" );
    803                 addIdArg( args, id );
    804                 tr_bencDictAddBool( args, "honorsSessionLimits", FALSE );
    805                 break;
    806 
    807             case 990:
    808                 tr_bencDictAddStr( &top, "method", "session-set" );
    809                 tr_bencDictAddBool( args, TR_PREFS_KEY_START, FALSE );
    810                 break;
    811 
    812             case 991:
    813                 tr_bencDictAddStr( &top, "method", "session-set" );
    814                 tr_bencDictAddBool( args, TR_PREFS_KEY_START, TRUE );
    815                 break;
    816 
    817             case 992:
    818                 tr_bencDictAddStr( &top, "method", "session-set" );
    819                 tr_bencDictAddBool( args, TR_PREFS_KEY_TRASH_ORIGINAL, TRUE );
    820                 break;
    821 
    822             case 993:
    823                 tr_bencDictAddStr( &top, "method", "session-set" );
    824                 tr_bencDictAddBool( args, TR_PREFS_KEY_TRASH_ORIGINAL, FALSE );
    825                 break;
    826 
    827             case TR_OPT_ERR:
    828                 fprintf( stderr, "invalid option\n" );
    829                 showUsage( );
    830                 status |= EXIT_FAILURE;
    831                 break;
    832 
    833             default:
    834                 fprintf( stderr, "got opt [%d]\n", (int)c );
    835                 showUsage( );
    836                 break;
    837         }
    838 
    839         if( addArg )
    840         {
    841             reqs[reqCount++] = tr_bencToStr( &top, TR_FMT_JSON_LEAN, NULL );
    842         }
    843 
    844         tr_bencFree( &top );
    845     }
    846 
    847     return status;
    848 }
    849 
    850 /* [host:port] or [host] or [port] */
    851 static void
    852 getHostAndPort( int * argc, char ** argv, char ** host, int * port )
    853 {
    854     if( *argv[1] != '-' )
    855     {
    856         int          i;
    857         const char * s = argv[1];
    858         const char * delim = strchr( s, ':' );
    859         if( delim )   /* user passed in both host and port */
    860         {
    861             *host = tr_strndup( s, delim - s );
    862             *port = atoi( delim + 1 );
    863         }
    864         else
    865         {
    866             char *    end;
    867             const int i = strtol( s, &end, 10 );
    868             if( !*end ) /* user passed in a port */
    869                 *port = i;
    870             else /* user passed in a host */
    871                 *host = tr_strdup( s );
    872         }
    873 
    874         *argc -= 1;
    875         for( i = 1; i < *argc; ++i )
    876             argv[i] = argv[i + 1];
    877     }
    878 }
    879 
    880636static size_t
    881 writeFunc( void * ptr,
    882            size_t size,
    883            size_t nmemb,
    884            void * buf )
     637writeFunc( void * ptr, size_t size, size_t nmemb, void * buf )
    885638{
    886639    const size_t byteCount = size * nmemb;
    887 
    888640    evbuffer_add( buf, ptr, byteCount );
    889641    return byteCount;
    890642}
    891643
    892 
    893 static void
    894 etaToString( char *  buf, size_t  buflen, int64_t eta )
    895 {
    896     if( eta < 0 )
    897         tr_snprintf( buf, buflen, "Unknown" );
    898     else if( eta < 60 )
    899         tr_snprintf( buf, buflen, "%" PRId64 "sec", eta );
    900     else if( eta < ( 60 * 60 ) )
    901         tr_snprintf( buf, buflen, "%" PRId64 " min", eta / 60 );
    902     else if( eta < ( 60 * 60 * 24 ) )
    903         tr_snprintf( buf, buflen, "%" PRId64 " hrs", eta / ( 60 * 60 ) );
    904     else
    905         tr_snprintf( buf, buflen, "%" PRId64 " days", eta / ( 60 * 60 * 24 ) );
    906 }
    907 
    908 static char*
    909 tr_strltime( char * buf, int seconds, size_t buflen )
    910 {
    911     int  days, hours, minutes;
    912     char d[128], h[128], m[128], s[128];
    913 
    914     if( seconds < 0 )
    915         seconds = 0;
    916 
    917     days = seconds / 86400;
    918     hours = ( seconds % 86400 ) / 3600;
    919     minutes = ( seconds % 3600 ) / 60;
    920     seconds = ( seconds % 3600 ) % 60;
    921 
    922     tr_snprintf( d, sizeof( d ), "%'d day%s", days, days==1?"":"s" );
    923     tr_snprintf( h, sizeof( h ), "%'d hour%s", hours, hours==1?"":"s" );
    924     tr_snprintf( m, sizeof( m ), "%'d minute%s", minutes, minutes==1?"":"s" );
    925     tr_snprintf( s, sizeof( s ), "%'d second%s", seconds, seconds==1?"":"s" );
    926 
    927     if( days )
    928     {
    929         if( days >= 4 || !hours )
    930             tr_strlcpy( buf, d, buflen );
    931         else
    932             tr_snprintf( buf, buflen, "%s, %s", d, h );
    933     }
    934     else if( hours )
    935     {
    936         if( hours >= 4 || !minutes )
    937             tr_strlcpy( buf, h, buflen );
    938         else
    939             tr_snprintf( buf, buflen, "%s, %s", h, m );
    940     }
    941     else if( minutes )
    942     {
    943         if( minutes >= 4 || !seconds )
    944             tr_strlcpy( buf, m, buflen );
    945         else
    946             tr_snprintf( buf, buflen, "%s, %s", m, s );
    947     }
    948     else tr_strlcpy( buf, s, buflen );
    949 
    950     return buf;
    951 }
    952 
    953 #define KILOBYTE_FACTOR 1024.0
    954 #define MEGABYTE_FACTOR ( 1024.0 * 1024.0 )
    955 #define GIGABYTE_FACTOR ( 1024.0 * 1024.0 * 1024.0 )
    956 
    957 static char*
    958 strlratio2( char * buf, double ratio, size_t buflen )
    959 {
    960     return tr_strratio( buf, buflen, ratio, "Inf" );
    961 }
    962 
    963 static char*
    964 strlratio( char * buf,
    965            double numerator,
    966            double denominator,
    967            size_t buflen )
    968 {
    969     double ratio;
    970 
    971     if( denominator )
    972         ratio = numerator / denominator;
    973     else if( numerator )
    974         ratio = TR_RATIO_INF;
    975     else
    976         ratio = TR_RATIO_NA;
    977 
    978     return strlratio2( buf, ratio, buflen );
    979 }
    980 
    981 static char*
    982 strlsize( char *  buf, int64_t size, size_t  buflen )
    983 {
    984     if( !size )
    985         tr_strlcpy( buf, "None", buflen );
    986     else if( size < (int64_t)KILOBYTE_FACTOR )
    987         tr_snprintf( buf, buflen, "%'" PRId64 " bytes", (int64_t)size );
    988     else
    989     {
    990         double displayed_size;
    991         if( size < (int64_t)MEGABYTE_FACTOR )
    992         {
    993             displayed_size = (double) size / KILOBYTE_FACTOR;
    994             tr_snprintf( buf, buflen, "%'.1f KiB", displayed_size );
    995         }
    996         else if( size < (int64_t)GIGABYTE_FACTOR )
    997         {
    998             displayed_size = (double) size / MEGABYTE_FACTOR;
    999             tr_snprintf( buf, buflen, "%'.1f MiB", displayed_size );
    1000         }
    1001         else
    1002         {
    1003             displayed_size = (double) size / GIGABYTE_FACTOR;
    1004             tr_snprintf( buf, buflen, "%'.1f GiB", displayed_size );
    1005         }
    1006     }
    1007     return buf;
     644/* look for a session id in the header in case the server gives back a 409 */
     645static size_t
     646parseResponseHeader( void *ptr, size_t size, size_t nmemb, void * stream UNUSED )
     647{
     648    const char * line = ptr;
     649    const size_t line_len = size * nmemb;
     650    const char * key = TR_RPC_SESSION_ID_HEADER ": ";
     651    const size_t key_len = strlen( key );
     652
     653    if( ( line_len >= key_len ) && !memcmp( line, key, key_len ) )
     654    {
     655        const char * begin = line + key_len;
     656        const char * end = begin;
     657        while( !isspace( *end ) )
     658            ++end;
     659        tr_free( sessionId );
     660        sessionId = tr_strndup( begin, end-begin );
     661    }
     662
     663    return line_len;
     664}
     665
     666static long
     667getTimeoutSecs( const char * req )
     668{
     669  if( strstr( req, "\"method\":\"blocklist-update\"" ) != NULL )
     670    return 300L;
     671
     672  return 60L; /* default value */
    1008673}
    1009674
     
    1066731
    1067732    return buf;
    1068 }
    1069 
    1070 static void
    1071 printSession( tr_benc * top )
    1072 {
    1073     tr_benc *args;
    1074     if( ( tr_bencDictFindDict( top, "arguments", &args ) ) )
    1075     {
    1076         const char * str;
    1077         int64_t      i;
    1078         tr_bool      boolVal;
    1079 
    1080         printf( "VERSION\n" );
    1081         if( tr_bencDictFindStr( args,  "version", &str ) )
    1082             printf( "  Daemon version: %s\n", str );
    1083         if( tr_bencDictFindInt( args, "rpc-version", &i ) )
    1084             printf( "  RPC version: %" PRId64 "\n", i );
    1085         if( tr_bencDictFindInt( args, "rpc-version-minimum", &i ) )
    1086             printf( "  RPC minimum version: %" PRId64 "\n", i );
    1087         printf( "\n" );
    1088 
    1089         printf( "CONFIG\n" );
    1090         if( tr_bencDictFindStr( args, "config-dir", &str ) )
    1091             printf( "  Configuration directory: %s\n", str );
    1092         if( tr_bencDictFindStr( args,  TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
    1093             printf( "  Download directory: %s\n", str );
    1094         if( tr_bencDictFindInt( args, TR_PREFS_KEY_PEER_PORT, &i ) )
    1095             printf( "  Listenport: %" PRId64 "\n", i );
    1096         if( tr_bencDictFindBool( args, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) )
    1097             printf( "  Portforwarding enabled: %s\n", ( boolVal ? "Yes" : "No" ) );
    1098         if( tr_bencDictFindBool( args, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) )
    1099             printf( "  Peer exchange allowed: %s\n", ( boolVal ? "Yes" : "No" ) );
    1100         if( tr_bencDictFindStr( args,  TR_PREFS_KEY_ENCRYPTION, &str ) )
    1101             printf( "  Encryption: %s\n", str );
    1102         printf( "\n" );
    1103 
    1104         {
    1105             tr_bool altEnabled, altTimeEnabled, upEnabled, downEnabled, seedRatioLimited;
    1106             int64_t altDown, altUp, altBegin, altEnd, altDay, upLimit, downLimit, peerLimit;
    1107             double seedRatioLimit;
    1108 
    1109             if( tr_bencDictFindInt ( args, TR_PREFS_KEY_ALT_SPEED_DOWN, &altDown ) &&
    1110                 tr_bencDictFindBool( args, TR_PREFS_KEY_ALT_SPEED_ENABLED, &altEnabled ) &&
    1111                 tr_bencDictFindInt ( args, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &altBegin ) &&
    1112                 tr_bencDictFindBool( args, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &altTimeEnabled ) &&
    1113                 tr_bencDictFindInt ( args, TR_PREFS_KEY_ALT_SPEED_TIME_END, &altEnd ) &&
    1114                 tr_bencDictFindInt ( args, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &altDay ) &&
    1115                 tr_bencDictFindInt ( args, TR_PREFS_KEY_ALT_SPEED_UP, &altUp ) &&
    1116                 tr_bencDictFindInt ( args, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &peerLimit ) &&
    1117                 tr_bencDictFindInt ( args, TR_PREFS_KEY_DSPEED, &downLimit ) &&
    1118                 tr_bencDictFindBool( args, TR_PREFS_KEY_DSPEED_ENABLED, &downEnabled ) &&
    1119                 tr_bencDictFindInt ( args, TR_PREFS_KEY_USPEED, &upLimit ) &&
    1120                 tr_bencDictFindBool( args, TR_PREFS_KEY_USPEED_ENABLED, &upEnabled ) &&
    1121                 tr_bencDictFindReal( args, "seedRatioLimit", &seedRatioLimit ) &&
    1122                 tr_bencDictFindBool( args, "seedRatioLimited", &seedRatioLimited) )
    1123             {
    1124                 char buf[128];
    1125 
    1126                 printf( "LIMITS\n" );
    1127                 printf( "  Peer limit: %" PRId64 "\n", peerLimit );
    1128 
    1129                 if( seedRatioLimited )
    1130                     tr_snprintf( buf, sizeof( buf ), "%.2f", seedRatioLimit );
    1131                 else
    1132                     tr_strlcpy( buf, "Unlimited", sizeof( buf ) );
    1133                 printf( "  Default seed ratio limit: %s\n", buf );
    1134 
    1135                 if( altEnabled )
    1136                     tr_snprintf( buf, sizeof( buf ), "%"PRId64" KiB/s", altUp );
    1137                 else if( upEnabled )
    1138                     tr_snprintf( buf, sizeof( buf ), "%"PRId64" KiB/s", upLimit );
    1139                 else
    1140                     tr_strlcpy( buf, "Unlimited", sizeof( buf ) );
    1141                 printf( "  Upload speed limit: %s  (%s limit: %"PRId64" KiB/s; %s turtle limit: %"PRId64" KiB/s)\n",
    1142                         buf,
    1143                         (upEnabled?"Enabled":"Disabled"), upLimit,
    1144                         (altEnabled?"Enabled":"Disabled"), altUp );
    1145 
    1146                 if( altEnabled )
    1147                     tr_snprintf( buf, sizeof( buf ), "%"PRId64" KiB/s", altDown );
    1148                 else if( downEnabled )
    1149                     tr_snprintf( buf, sizeof( buf ), "%"PRId64" KiB/s", downLimit );
    1150                 else
    1151                     tr_strlcpy( buf, "Unlimited", sizeof( buf ) );
    1152                 printf( "  Download speed limit: %s  (%s limit: %"PRId64" KiB/s; %s turtle limit: %"PRId64" KiB/s)\n",
    1153                         buf,
    1154                         (downEnabled?"Enabled":"Disabled"), downLimit,
    1155                         (altEnabled?"Enabled":"Disabled"), altDown );
    1156 
    1157                 if( altTimeEnabled ) {
    1158                     printf( "  Turtle schedule: %02d:%02d - %02d:%02d  ",
    1159                             (int)(altBegin/60), (int)(altBegin%60),
    1160                             (int)(altEnd/60), (int)(altEnd%60) );
    1161                     if( altDay & TR_SCHED_SUN )   printf( "Sun " );
    1162                     if( altDay & TR_SCHED_MON )   printf( "Mon " );
    1163                     if( altDay & TR_SCHED_TUES )  printf( "Tue " );
    1164                     if( altDay & TR_SCHED_WED )   printf( "Wed " );
    1165                     if( altDay & TR_SCHED_THURS ) printf( "Thu " );
    1166                     if( altDay & TR_SCHED_FRI )   printf( "Fri " );
    1167                     if( altDay & TR_SCHED_SAT )   printf( "Sat " );
    1168                     printf( "\n" );
    1169                 }
    1170             }
    1171         }
    1172         printf( "\n" );
    1173 
    1174         printf( "MISC\n" );
    1175         if( tr_bencDictFindBool( args, TR_PREFS_KEY_START, &boolVal ) )
    1176             printf( "  Autostart added torrents: %s\n", ( boolVal ? "Yes" : "No" ) );
    1177         if( tr_bencDictFindBool( args, TR_PREFS_KEY_TRASH_ORIGINAL, &boolVal ) )
    1178             printf( "  Delete automatically added torrents: %s\n", ( boolVal ? "Yes" : "No" ) );
    1179     }
    1180 }
    1181 
    1182 static void
    1183 printSessionStats( tr_benc * top )
    1184 {
    1185     tr_benc *args, *d;
    1186     if( ( tr_bencDictFindDict( top, "arguments", &args ) ) )
    1187     {
    1188         char buf[512];
    1189         int64_t up, down, secs, sessions;
    1190 
    1191         if( tr_bencDictFindDict( args, "current-stats", &d )
    1192             && tr_bencDictFindInt( d, "uploadedBytes", &up )
    1193             && tr_bencDictFindInt( d, "downloadedBytes", &down )
    1194             && tr_bencDictFindInt( d, "secondsActive", &secs ) )
    1195         {
    1196             printf( "\nCURRENT SESSION\n" );
    1197             printf( "  Uploaded:   %s\n", strlsize( buf, up, sizeof( buf ) ) );
    1198             printf( "  Downloaded: %s\n", strlsize( buf, down, sizeof( buf ) ) );
    1199             printf( "  Ratio:      %s\n", strlratio( buf, up, down, sizeof( buf ) ) );
    1200             printf( "  Duration:   %s\n", tr_strltime( buf, secs, sizeof( buf ) ) );
    1201         }
    1202 
    1203         if( tr_bencDictFindDict( args, "cumulative-stats", &d )
    1204             && tr_bencDictFindInt( d, "sessionCount", &sessions )
    1205             && tr_bencDictFindInt( d, "uploadedBytes", &up )
    1206             && tr_bencDictFindInt( d, "downloadedBytes", &down )
    1207             && tr_bencDictFindInt( d, "secondsActive", &secs ) )
    1208         {
    1209             printf( "\nTOTAL\n" );
    1210             printf( "  Started %lu times\n", (unsigned long)sessions );
    1211             printf( "  Uploaded:   %s\n", strlsize( buf, up, sizeof( buf ) ) );
    1212             printf( "  Downloaded: %s\n", strlsize( buf, down, sizeof( buf ) ) );
    1213             printf( "  Ratio:      %s\n", strlratio( buf, up, down, sizeof( buf ) ) );
    1214             printf( "  Duration:   %s\n", tr_strltime( buf, secs, sizeof( buf ) ) );
    1215         }
    1216     }
    1217733}
    1218734
     
    17921308}
    17931309
     1310static void
     1311printSession( tr_benc * top )
     1312{
     1313    tr_benc *args;
     1314    if( ( tr_bencDictFindDict( top, "arguments", &args ) ) )
     1315    {
     1316        const char * str;
     1317        int64_t      i;
     1318        tr_bool      boolVal;
     1319
     1320        printf( "VERSION\n" );
     1321        if( tr_bencDictFindStr( args,  "version", &str ) )
     1322            printf( "  Daemon version: %s\n", str );
     1323        if( tr_bencDictFindInt( args, "rpc-version", &i ) )
     1324            printf( "  RPC version: %" PRId64 "\n", i );
     1325        if( tr_bencDictFindInt( args, "rpc-version-minimum", &i ) )
     1326            printf( "  RPC minimum version: %" PRId64 "\n", i );
     1327        printf( "\n" );
     1328
     1329        printf( "CONFIG\n" );
     1330        if( tr_bencDictFindStr( args, "config-dir", &str ) )
     1331            printf( "  Configuration directory: %s\n", str );
     1332        if( tr_bencDictFindStr( args,  TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
     1333            printf( "  Download directory: %s\n", str );
     1334        if( tr_bencDictFindInt( args, TR_PREFS_KEY_PEER_PORT, &i ) )
     1335            printf( "  Listenport: %" PRId64 "\n", i );
     1336        if( tr_bencDictFindBool( args, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) )
     1337            printf( "  Portforwarding enabled: %s\n", ( boolVal ? "Yes" : "No" ) );
     1338        if( tr_bencDictFindBool( args, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) )
     1339            printf( "  Peer exchange allowed: %s\n", ( boolVal ? "Yes" : "No" ) );
     1340        if( tr_bencDictFindStr( args,  TR_PREFS_KEY_ENCRYPTION, &str ) )
     1341            printf( "  Encryption: %s\n", str );
     1342        printf( "\n" );
     1343
     1344        {
     1345            tr_bool altEnabled, altTimeEnabled, upEnabled, downEnabled, seedRatioLimited;
     1346            int64_t altDown, altUp, altBegin, altEnd, altDay, upLimit, downLimit, peerLimit;
     1347            double seedRatioLimit;
     1348
     1349            if( tr_bencDictFindInt ( args, TR_PREFS_KEY_ALT_SPEED_DOWN, &altDown ) &&
     1350                tr_bencDictFindBool( args, TR_PREFS_KEY_ALT_SPEED_ENABLED, &altEnabled ) &&
     1351                tr_bencDictFindInt ( args, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &altBegin ) &&
     1352                tr_bencDictFindBool( args, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &altTimeEnabled ) &&
     1353                tr_bencDictFindInt ( args, TR_PREFS_KEY_ALT_SPEED_TIME_END, &altEnd ) &&
     1354                tr_bencDictFindInt ( args, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &altDay ) &&
     1355                tr_bencDictFindInt ( args, TR_PREFS_KEY_ALT_SPEED_UP, &altUp ) &&
     1356                tr_bencDictFindInt ( args, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &peerLimit ) &&
     1357                tr_bencDictFindInt ( args, TR_PREFS_KEY_DSPEED, &downLimit ) &&
     1358                tr_bencDictFindBool( args, TR_PREFS_KEY_DSPEED_ENABLED, &downEnabled ) &&
     1359                tr_bencDictFindInt ( args, TR_PREFS_KEY_USPEED, &upLimit ) &&
     1360                tr_bencDictFindBool( args, TR_PREFS_KEY_USPEED_ENABLED, &upEnabled ) &&
     1361                tr_bencDictFindReal( args, "seedRatioLimit", &seedRatioLimit ) &&
     1362                tr_bencDictFindBool( args, "seedRatioLimited", &seedRatioLimited) )
     1363            {
     1364                char buf[128];
     1365
     1366                printf( "LIMITS\n" );
     1367                printf( "  Peer limit: %" PRId64 "\n", peerLimit );
     1368
     1369                if( seedRatioLimited )
     1370                    tr_snprintf( buf, sizeof( buf ), "%.2f", seedRatioLimit );
     1371                else
     1372                    tr_strlcpy( buf, "Unlimited", sizeof( buf ) );
     1373                printf( "  Default seed ratio limit: %s\n", buf );
     1374
     1375                if( altEnabled )
     1376                    tr_snprintf( buf, sizeof( buf ), "%"PRId64" KiB/s", altUp );
     1377                else if( upEnabled )
     1378                    tr_snprintf( buf, sizeof( buf ), "%"PRId64" KiB/s", upLimit );
     1379                else
     1380                    tr_strlcpy( buf, "Unlimited", sizeof( buf ) );
     1381                printf( "  Upload speed limit: %s  (%s limit: %"PRId64" KiB/s; %s turtle limit: %"PRId64" KiB/s)\n",
     1382                        buf,
     1383                        (upEnabled?"Enabled":"Disabled"), upLimit,
     1384                        (altEnabled?"Enabled":"Disabled"), altUp );
     1385
     1386                if( altEnabled )
     1387                    tr_snprintf( buf, sizeof( buf ), "%"PRId64" KiB/s", altDown );
     1388                else if( downEnabled )
     1389                    tr_snprintf( buf, sizeof( buf ), "%"PRId64" KiB/s", downLimit );
     1390                else
     1391                    tr_strlcpy( buf, "Unlimited", sizeof( buf ) );
     1392                printf( "  Download speed limit: %s  (%s limit: %"PRId64" KiB/s; %s turtle limit: %"PRId64" KiB/s)\n",
     1393                        buf,
     1394                        (downEnabled?"Enabled":"Disabled"), downLimit,
     1395                        (altEnabled?"Enabled":"Disabled"), altDown );
     1396
     1397                if( altTimeEnabled ) {
     1398                    printf( "  Turtle schedule: %02d:%02d - %02d:%02d  ",
     1399                            (int)(altBegin/60), (int)(altBegin%60),
     1400                            (int)(altEnd/60), (int)(altEnd%60) );
     1401                    if( altDay & TR_SCHED_SUN )   printf( "Sun " );
     1402                    if( altDay & TR_SCHED_MON )   printf( "Mon " );
     1403                    if( altDay & TR_SCHED_TUES )  printf( "Tue " );
     1404                    if( altDay & TR_SCHED_WED )   printf( "Wed " );
     1405                    if( altDay & TR_SCHED_THURS ) printf( "Thu " );
     1406                    if( altDay & TR_SCHED_FRI )   printf( "Fri " );
     1407                    if( altDay & TR_SCHED_SAT )   printf( "Sat " );
     1408                    printf( "\n" );
     1409                }
     1410            }
     1411        }
     1412        printf( "\n" );
     1413
     1414        printf( "MISC\n" );
     1415        if( tr_bencDictFindBool( args, TR_PREFS_KEY_START, &boolVal ) )
     1416            printf( "  Autostart added torrents: %s\n", ( boolVal ? "Yes" : "No" ) );
     1417        if( tr_bencDictFindBool( args, TR_PREFS_KEY_TRASH_ORIGINAL, &boolVal ) )
     1418            printf( "  Delete automatically added torrents: %s\n", ( boolVal ? "Yes" : "No" ) );
     1419    }
     1420}
     1421
     1422static void
     1423printSessionStats( tr_benc * top )
     1424{
     1425    tr_benc *args, *d;
     1426    if( ( tr_bencDictFindDict( top, "arguments", &args ) ) )
     1427    {
     1428        char buf[512];
     1429        int64_t up, down, secs, sessions;
     1430
     1431        if( tr_bencDictFindDict( args, "current-stats", &d )
     1432            && tr_bencDictFindInt( d, "uploadedBytes", &up )
     1433            && tr_bencDictFindInt( d, "downloadedBytes", &down )
     1434            && tr_bencDictFindInt( d, "secondsActive", &secs ) )
     1435        {
     1436            printf( "\nCURRENT SESSION\n" );
     1437            printf( "  Uploaded:   %s\n", strlsize( buf, up, sizeof( buf ) ) );
     1438            printf( "  Downloaded: %s\n", strlsize( buf, down, sizeof( buf ) ) );
     1439            printf( "  Ratio:      %s\n", strlratio( buf, up, down, sizeof( buf ) ) );
     1440            printf( "  Duration:   %s\n", tr_strltime( buf, secs, sizeof( buf ) ) );
     1441        }
     1442
     1443        if( tr_bencDictFindDict( args, "cumulative-stats", &d )
     1444            && tr_bencDictFindInt( d, "sessionCount", &sessions )
     1445            && tr_bencDictFindInt( d, "uploadedBytes", &up )
     1446            && tr_bencDictFindInt( d, "downloadedBytes", &down )
     1447            && tr_bencDictFindInt( d, "secondsActive", &secs ) )
     1448        {
     1449            printf( "\nTOTAL\n" );
     1450            printf( "  Started %lu times\n", (unsigned long)sessions );
     1451            printf( "  Uploaded:   %s\n", strlsize( buf, up, sizeof( buf ) ) );
     1452            printf( "  Downloaded: %s\n", strlsize( buf, down, sizeof( buf ) ) );
     1453            printf( "  Ratio:      %s\n", strlratio( buf, up, down, sizeof( buf ) ) );
     1454            printf( "  Duration:   %s\n", tr_strltime( buf, secs, sizeof( buf ) ) );
     1455        }
     1456    }
     1457}
     1458
     1459static char id[4096];
     1460
    17941461static int
    1795 processResponse( const char * host,
    1796                  int          port,
    1797                  const void * response,
    1798                  size_t       len )
     1462processResponse( const char * host, int port, const void * response, size_t len )
    17991463{
    18001464    tr_benc top;
     
    18391503            case TAG_PORTTEST:
    18401504                printPortTest( &top ); break;
     1505
     1506            case TAG_TORRENT_ADD: {
     1507                int64_t i;
     1508                tr_benc * b = &top;
     1509                if( tr_bencDictFindDict( &top, ARGUMENTS, &b )
     1510                        && tr_bencDictFindDict( b, "torrent-added", &b )
     1511                        && tr_bencDictFindInt( b, "id", &i ) )
     1512                    tr_snprintf( id, sizeof(id), "%"PRId64, i );
     1513                break;
     1514            }
    18411515
    18421516            default:
     
    18541528
    18551529    return status;
    1856 }
    1857 
    1858 /* look for a session id in the header in case the server gives back a 409 */
    1859 static size_t
    1860 parseResponseHeader( void *ptr, size_t size, size_t nmemb, void * stream UNUSED )
    1861 {
    1862     const char * line = ptr;
    1863     const size_t line_len = size * nmemb;
    1864     const char * key = TR_RPC_SESSION_ID_HEADER ": ";
    1865     const size_t key_len = strlen( key );
    1866 
    1867     if( ( line_len >= key_len ) && !memcmp( line, key, key_len ) )
    1868     {
    1869         const char * begin = line + key_len;
    1870         const char * end = begin;
    1871         while( !isspace( *end ) )
    1872             ++end;
    1873         tr_free( sessionId );
    1874         sessionId = tr_strndup( begin, end-begin );
    1875     }
    1876 
    1877     return line_len;
    18781530}
    18791531
     
    19041556}
    19051557
    1906 static long
    1907 getTimeoutSecs( const char * req )
    1908 {
    1909   if( strstr( req, "\"method\":\"blocklist-update\"" ) != NULL )
    1910     return 300L;
    1911 
    1912   return 60L; /* default value */
    1913 }
    1914 
    19151558static int
    1916 processRequests( const char *  host,
    1917                  int           port,
    1918                  const char ** reqs,
    1919                  int           reqCount )
    1920 {
    1921     int i;
    1922     CURL * curl = NULL;
     1559flush( const char * host, int port, tr_benc ** benc )
     1560{
     1561    CURLcode res;
     1562    CURL * curl;
     1563    int status = EXIT_SUCCESS;
    19231564    struct evbuffer * buf = evbuffer_new( );
     1565    char * json = tr_bencToStr( *benc, TR_FMT_JSON_LEAN, NULL );
    19241566    char * url = tr_strdup_printf( "http://%s:%d/transmission/rpc", host, port );
     1567
     1568    curl = tr_curl_easy_init( buf );
     1569    curl_easy_setopt( curl, CURLOPT_URL, url );
     1570    curl_easy_setopt( curl, CURLOPT_POSTFIELDS, json );
     1571    curl_easy_setopt( curl, CURLOPT_TIMEOUT, getTimeoutSecs( json ) );
     1572
     1573    if( debug )
     1574        fprintf( stderr, "posting:\n--------\n%s\n--------\n", json );
     1575
     1576    if(( res = curl_easy_perform( curl )))
     1577    {
     1578        tr_nerr( MY_NAME, "(%s) %s", url, curl_easy_strerror( res ) );
     1579        status |= EXIT_FAILURE;
     1580    }
     1581    else
     1582    {
     1583        long response;
     1584        curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &response );
     1585        switch( response ) {
     1586            case 200:
     1587                status |= processResponse( host, port, EVBUFFER_DATA(buf), EVBUFFER_LENGTH(buf) );
     1588                break;
     1589            case 409:
     1590                /* session id failed.  our curl header func has already
     1591                * pulled the new session id from this response's headers,
     1592                * build a new CURL* and try again */
     1593                curl_easy_cleanup( curl );
     1594                curl = NULL;
     1595                flush( host, port, benc );
     1596                benc = NULL;
     1597                break;
     1598            default:
     1599                fprintf( stderr, "Unexpected response: %s\n", (char*)EVBUFFER_DATA(buf) );
     1600                status |= EXIT_FAILURE;
     1601                break;
     1602        }
     1603    }
     1604
     1605    /* cleanup */
     1606    tr_free( url );
     1607    tr_free( json );
     1608    evbuffer_free( buf );
     1609    if( curl != 0 )
     1610        curl_easy_cleanup( curl );
     1611    if( benc != NULL ) {
     1612        tr_bencFree( *benc );
     1613        *benc = 0;
     1614    }
     1615    return status;
     1616}
     1617
     1618static tr_benc*
     1619ensure_sset( tr_benc ** sset )
     1620{
     1621    tr_benc * args;
     1622
     1623    if( *sset )
     1624        args = tr_bencDictFind( *sset, ARGUMENTS );
     1625    else {
     1626        *sset = tr_new0( tr_benc, 1 );
     1627        tr_bencInitDict( *sset, 3 );
     1628        tr_bencDictAddStr( *sset, "method", "session-set" );
     1629        args = tr_bencDictAddDict( *sset, ARGUMENTS, 0 );
     1630    }
     1631
     1632    return args;
     1633}
     1634
     1635static tr_benc*
     1636ensure_tset( tr_benc ** tset )
     1637{
     1638    tr_benc * args;
     1639
     1640    if( *tset )
     1641        args = tr_bencDictFind( *tset, ARGUMENTS );
     1642    else {
     1643        *tset = tr_new0( tr_benc, 1 );
     1644        tr_bencInitDict( *tset, 3 );
     1645        tr_bencDictAddStr( *tset, "method", "torrent-set" );
     1646        args = tr_bencDictAddDict( *tset, ARGUMENTS, 1 );
     1647    }
     1648
     1649    return args;
     1650}
     1651
     1652static int
     1653processArgs( const char * host, int port, int argc, const char ** argv )
     1654{
     1655    int c;
    19251656    int status = EXIT_SUCCESS;
    1926 
    1927     for( i=0; i<reqCount; ++i )
    1928     {
    1929         CURLcode res;
    1930         evbuffer_drain( buf, EVBUFFER_LENGTH( buf ) );
    1931 
    1932         if( curl == NULL )
    1933         {
    1934             curl = tr_curl_easy_init( buf );
    1935             curl_easy_setopt( curl, CURLOPT_URL, url );
    1936         }
    1937 
    1938         curl_easy_setopt( curl, CURLOPT_POSTFIELDS, reqs[i] );
    1939         curl_easy_setopt( curl, CURLOPT_TIMEOUT, getTimeoutSecs( reqs[i] ) );
    1940 
    1941         if( debug )
    1942             fprintf( stderr, "posting:\n--------\n%s\n--------\n", reqs[i] );
    1943         if( ( res = curl_easy_perform( curl ) ) )
    1944         {
    1945             tr_nerr( MY_NAME, "(%s:%d) %s", host, port, curl_easy_strerror( res ) );
    1946             status |= EXIT_FAILURE;
    1947         }
    1948         else {
    1949             long response;
    1950             curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &response );
    1951             switch( response ) {
    1952                 case 200:
    1953                     status |= processResponse( host, port, EVBUFFER_DATA(buf), EVBUFFER_LENGTH(buf) );
     1657    const char * optarg;
     1658    tr_benc *sset = 0;
     1659    tr_benc *tset = 0;
     1660    tr_benc *tadd = 0;
     1661
     1662    *id = '\0';
     1663
     1664    while(( c = tr_getopt( getUsage( ), argc, argv, opts, &optarg )))
     1665    {
     1666        const int stepMode = getOptMode( c );
     1667
     1668        if( !stepMode ) /* meta commands */
     1669        {
     1670            switch( c )
     1671            {
     1672                case 'a': /* add torrent */
     1673                    if( tadd != 0 ) flush( host, port, &tadd );
     1674                    if( tset != 0 ) { addIdArg( tr_bencDictFind( tset, ARGUMENTS ), id ); flush( host, port, &tset ); }
     1675                    tadd = tr_new0( tr_benc, 1 );
     1676                    tr_bencInitDict( tadd, 3 );
     1677                    tr_bencDictAddStr( tadd, "method", "torrent-add" );
     1678                    tr_bencDictAddInt( tadd, "tag", TAG_TORRENT_ADD );
     1679                    tr_bencDictAddDict( tadd, ARGUMENTS, 0 );
    19541680                    break;
    1955                 case 409:
    1956                     /* session id failed.  our curl header func has already
    1957                      * pulled the new session id from this response's headers,
    1958                      * build a new CURL* and try again */
    1959                     curl_easy_cleanup( curl );
    1960                     curl = NULL;
    1961                     --i;
     1681
     1682                case 'b': /* debug */
     1683                    debug = TRUE;
    19621684                    break;
    1963                 default:
    1964                     fprintf( stderr, "Unexpected response: %s\n", (char*)EVBUFFER_DATA(buf) );
     1685
     1686                case 'n': /* auth */
     1687                    auth = tr_strdup( optarg );
     1688                    break;
     1689
     1690                case 'N': /* netrc */
     1691                    netrc = tr_strdup( optarg );
     1692                    break;
     1693
     1694                case 't': /* set current torrent */
     1695                    if( tadd != 0 ) flush( host, port, &tadd );
     1696                    if( tset != 0 ) { addIdArg( tr_bencDictFind( tset, ARGUMENTS ), id ); flush( host, port, &tset ); }
     1697                    tr_strlcpy( id, optarg, sizeof( id ) );
     1698                    break;
     1699
     1700                case 'V': /* show version number */
     1701                    fprintf( stderr, "Transmission %s\n", LONG_VERSION_STRING );
     1702                    exit( 0 );
     1703                    break;
     1704
     1705                case TR_OPT_ERR:
     1706                    fprintf( stderr, "invalid option\n" );
     1707                    showUsage( );
    19651708                    status |= EXIT_FAILURE;
    19661709                    break;
    1967             }
    1968         }
    1969     }
    1970 
    1971     /* cleanup */
    1972     tr_free( url );
    1973     evbuffer_free( buf );
    1974     if( curl != NULL )
    1975         curl_easy_cleanup( curl );
     1710
     1711                case TR_OPT_UNK:
     1712                    if( tadd ) {
     1713                        tr_benc * args = tr_bencDictFind( tadd, ARGUMENTS );
     1714                        char * tmp = getEncodedMetainfo( optarg );
     1715                        if( tmp )
     1716                            tr_bencDictAddStr( args, "metainfo", tmp );
     1717                        else
     1718                            tr_bencDictAddStr( args, "filename", optarg );
     1719                        tr_free( tmp );
     1720                    } else {
     1721                        fprintf( stderr, "Unknown option: %s\n", optarg );
     1722                        status |= EXIT_FAILURE;
     1723                    }
     1724                    break;
     1725            }
     1726        }
     1727        else if( stepMode == MODE_TORRENT_GET )
     1728        {
     1729            size_t i, n;
     1730            tr_benc * top = tr_new0( tr_benc, 1 );
     1731            tr_benc * args;
     1732            tr_benc * fields;
     1733            tr_bencInitDict( top, 3 );
     1734            tr_bencDictAddStr( top, "method", "torrent-get" );
     1735            args = tr_bencDictAddDict( top, ARGUMENTS, 0 );
     1736            fields = tr_bencDictAddList( args, "fields", 0 );
     1737
     1738            if( tset != 0 ) { addIdArg( tr_bencDictFind( tset, ARGUMENTS ), id ); flush( host, port, &tset ); }
     1739           
     1740            switch( c )
     1741            {
     1742                case 'f': tr_bencDictAddInt( top, "tag", TAG_FILES );
     1743                          n = TR_N_ELEMENTS( files_keys );
     1744                          for( i=0; i<n; ++i ) tr_bencListAddStr( fields, files_keys[i] );
     1745                          addIdArg( args, id );
     1746                          break;
     1747                case 'i': tr_bencDictAddInt( top, "tag", TAG_DETAILS );
     1748                          n = TR_N_ELEMENTS( details_keys );
     1749                          for( i=0; i<n; ++i ) tr_bencListAddStr( fields, details_keys[i] );
     1750                          addIdArg( args, id );
     1751                          break;
     1752                case 'l': tr_bencDictAddInt( top, "tag", TAG_LIST );
     1753                          n = TR_N_ELEMENTS( list_keys );
     1754                          for( i=0; i<n; ++i ) tr_bencListAddStr( fields, list_keys[i] );
     1755                          break;
     1756                case 940: tr_bencDictAddInt( top, "tag", TAG_PEERS );
     1757                          tr_bencListAddStr( fields, "peers" );
     1758                          addIdArg( args, id );
     1759                          break;
     1760                default:  assert( "unhandled value" && 0 );
     1761            }
     1762
     1763            flush( host, port, &top );
     1764        }
     1765        else if( stepMode == MODE_SESSION_SET )
     1766        {
     1767            tr_benc * args = ensure_sset( &sset );
     1768
     1769            switch( c )
     1770            {
     1771                case 970: tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_ENABLED, TRUE );
     1772                          break;
     1773                case 971: tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_ENABLED, FALSE );
     1774                          break;
     1775                case 972: tr_bencDictAddInt( args, TR_PREFS_KEY_ALT_SPEED_DOWN, numarg( optarg ) );
     1776                          break;
     1777                case 973: tr_bencDictAddInt( args, TR_PREFS_KEY_ALT_SPEED_UP, numarg( optarg ) );
     1778                          break;
     1779                case 974: tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, TRUE );
     1780                          break;
     1781                case 975: tr_bencDictAddBool( args, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, FALSE );
     1782                          break;
     1783                case 976: addTime( args, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, optarg );
     1784                          break;
     1785                case 977: addTime( args, TR_PREFS_KEY_ALT_SPEED_TIME_END, optarg );
     1786                          break;
     1787                case 978: addDays( args, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, optarg );
     1788                          break;
     1789                case 'c': tr_bencDictAddStr( args, TR_PREFS_KEY_INCOMPLETE_DIR, optarg );
     1790                          tr_bencDictAddBool( args, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, TRUE );
     1791                          break;
     1792                case 'C': tr_bencDictAddBool( args, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, FALSE );
     1793                          break;
     1794                case 910: tr_bencDictAddStr( args, TR_PREFS_KEY_ENCRYPTION, "required" );
     1795                          break;
     1796                case 911: tr_bencDictAddStr( args, TR_PREFS_KEY_ENCRYPTION, "preferred" );
     1797                          break;
     1798                case 912: tr_bencDictAddStr( args, TR_PREFS_KEY_ENCRYPTION, "tolerated" );
     1799                          break;
     1800                case 'm': tr_bencDictAddBool( args, TR_PREFS_KEY_PORT_FORWARDING, TRUE );
     1801                          break;
     1802                case 'M': tr_bencDictAddBool( args, TR_PREFS_KEY_PORT_FORWARDING, FALSE );
     1803                          break;
     1804                case 'o': tr_bencDictAddBool( args, TR_PREFS_KEY_DHT_ENABLED, TRUE );
     1805                          break;
     1806                case 'O': tr_bencDictAddBool( args, TR_PREFS_KEY_DHT_ENABLED, FALSE );
     1807                          break;
     1808                case 'p': tr_bencDictAddInt( args, TR_PREFS_KEY_PEER_PORT, numarg( optarg ) );
     1809                          break;
     1810                case 'P': tr_bencDictAddBool( args, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, TRUE);
     1811                          break;
     1812                case 'x': tr_bencDictAddBool( args, TR_PREFS_KEY_PEX_ENABLED, TRUE );
     1813                          break;
     1814                case 'X': tr_bencDictAddBool( args, TR_PREFS_KEY_PEX_ENABLED, FALSE );
     1815                          break;
     1816                case 931: tr_bencDictAddInt( args, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, atoi(optarg) );
     1817                          break;
     1818                case 953: tr_bencDictAddReal( args, "seedRatioLimit", atof(optarg) );
     1819                          tr_bencDictAddBool( args, "seedRatioLimited", TRUE );
     1820                          break;
     1821                case 954: tr_bencDictAddBool( args, "seedRatioLimited", FALSE );
     1822                          break;
     1823                case 990: tr_bencDictAddBool( args, TR_PREFS_KEY_START, FALSE );
     1824                          break;
     1825                case 991: tr_bencDictAddBool( args, TR_PREFS_KEY_START, TRUE );
     1826                          break;
     1827                case 992: tr_bencDictAddBool( args, TR_PREFS_KEY_TRASH_ORIGINAL, TRUE );
     1828                          break;
     1829                case 993: tr_bencDictAddBool( args, TR_PREFS_KEY_TRASH_ORIGINAL, FALSE );
     1830                          break;
     1831                default:  assert( "unhandled value" && 0 );
     1832                          break;
     1833            }
     1834        }
     1835        else if( stepMode == ( MODE_SESSION_SET | MODE_TORRENT_SET ) )
     1836        {
     1837            tr_benc * targs = 0;
     1838            tr_benc * sargs = 0;
     1839
     1840            if( *id )
     1841                targs = ensure_tset( &tset );
     1842            else
     1843                sargs = ensure_sset( &sset );
     1844           
     1845            switch( c )
     1846            {
     1847                case 'd': if( targs ) {
     1848                              tr_bencDictAddInt( targs, "downloadLimit", numarg( optarg ) );
     1849                              tr_bencDictAddBool( targs, "downloadLimited", TRUE );
     1850                          } else {
     1851                              tr_bencDictAddInt( sargs, TR_PREFS_KEY_DSPEED, numarg( optarg ) );
     1852                              tr_bencDictAddBool( sargs, TR_PREFS_KEY_DSPEED_ENABLED, TRUE );
     1853                          }
     1854                          break;
     1855                case 'D': if( targs )
     1856                              tr_bencDictAddBool( targs, "downloadLimited", FALSE );
     1857                          else
     1858                              tr_bencDictAddBool( sargs, TR_PREFS_KEY_DSPEED_ENABLED, TRUE );
     1859                          break;
     1860                case 'u': if( targs ) {
     1861                              tr_bencDictAddInt( targs, "uploadLimit", numarg( optarg ) );
     1862                              tr_bencDictAddBool( targs, "uploadLimited", TRUE );
     1863                          } else {
     1864                              tr_bencDictAddInt( sargs, TR_PREFS_KEY_USPEED, numarg( optarg ) );
     1865                              tr_bencDictAddBool( sargs, TR_PREFS_KEY_USPEED_ENABLED, TRUE );
     1866                          }
     1867                          break;
     1868                case 'U': if( targs )
     1869                              tr_bencDictAddBool( targs, "uploadLimited", FALSE );
     1870                          else
     1871                              tr_bencDictAddBool( sargs, TR_PREFS_KEY_USPEED_ENABLED, TRUE );
     1872                          break;
     1873                default:  assert( "unhandled value" && 0 );
     1874                          break;
     1875            }
     1876        }
     1877        else if( stepMode == MODE_TORRENT_SET )
     1878        {
     1879            tr_benc * args = ensure_tset( &tset );
     1880
     1881            switch( c )
     1882            {
     1883                case 930: tr_bencDictAddInt( args, "peer-limit", atoi(optarg) );
     1884                          break;
     1885                case 950: tr_bencDictAddReal( args, "seedRatioLimit", atof(optarg) );
     1886                          tr_bencDictAddInt( args, "seedRatioMode", TR_RATIOLIMIT_SINGLE );
     1887                          break;
     1888                case 951: tr_bencDictAddInt( args, "seedRatioMode", TR_RATIOLIMIT_GLOBAL );
     1889                          break;
     1890                case 952: tr_bencDictAddInt( args, "seedRatioMode", TR_RATIOLIMIT_UNLIMITED );
     1891                          break;
     1892                case 984: tr_bencDictAddBool( args, "honorsSessionLimits", TRUE );
     1893                          break;
     1894                case 985: tr_bencDictAddBool( args, "honorsSessionLimits", FALSE );
     1895                          break;
     1896                default:  assert( "unhandled value" && 0 );
     1897                          break;
     1898            }
     1899        }
     1900        else if( stepMode == ( MODE_TORRENT_SET | MODE_TORRENT_ADD ) )
     1901        {
     1902            tr_benc * args;
     1903
     1904            if( tadd )
     1905                args = tr_bencDictFind( tadd, ARGUMENTS );
     1906            else
     1907                args = ensure_tset( &tset );
     1908       
     1909            switch( c )
     1910            {         
     1911                case 'g': addFiles( args, "files-wanted", optarg );
     1912                          break;
     1913                case 'G': addFiles( args, "files-unwanted", optarg );
     1914                          break;
     1915                case 900: addFiles( args, "priority-high", optarg );
     1916                          break;
     1917                case 901: addFiles( args, "priority-normal", optarg );
     1918                          break;
     1919                case 902: addFiles( args, "priority-low", optarg );
     1920                          break;
     1921                default:  assert( "unhandled value" && 0 );
     1922                          break;
     1923            }
     1924        }
     1925        else if( c == 961 ) /* set location */
     1926        {
     1927            if( tadd )
     1928            {
     1929                tr_benc * args = tr_bencDictFind( tadd, ARGUMENTS );
     1930                tr_bencDictAddStr( args, "download-dir", optarg );
     1931            }
     1932            else
     1933            {
     1934                tr_benc * args;
     1935                tr_benc * top = tr_new0( tr_benc, 1 );
     1936                tr_bencInitDict( top, 2 );
     1937                tr_bencDictAddStr( top, "method", "torrent-set-location" );
     1938                args = tr_bencDictAddDict( top, ARGUMENTS, 3 );
     1939                tr_bencDictAddStr( args, "location", optarg );
     1940                tr_bencDictAddBool( args, "move", FALSE );
     1941                addIdArg( args, id );
     1942                flush( host, port, &top );
     1943                break;
     1944            }
     1945        }
     1946        else switch( c )
     1947        {
     1948            case 920: /* session-info */
     1949            {
     1950                tr_benc * top = tr_new0( tr_benc, 1 );
     1951                tr_bencInitDict( top, 2 );
     1952                tr_bencDictAddStr( top, "method", "session-get" );
     1953                tr_bencDictAddInt( top, "tag", TAG_SESSION );
     1954                flush( host, port, &top );
     1955                break;
     1956            }
     1957            case 's': /* start */
     1958            {
     1959                if( tadd )
     1960                    tr_bencDictAddBool( tr_bencDictFind( tadd, "arguments" ), "paused", FALSE );
     1961                else {
     1962                    tr_benc * top = tr_new0( tr_benc, 1 );
     1963                    tr_bencInitDict( top, 2 );
     1964                    tr_bencDictAddStr( top, "method", "torrent-start" );
     1965                    addIdArg( tr_bencDictAddDict( top, ARGUMENTS, 1 ), id );
     1966                    flush( host, port, &top );
     1967                }
     1968                break;
     1969            }
     1970            case 'S': /* stop */
     1971            {
     1972                if( tadd )
     1973                    tr_bencDictAddBool( tr_bencDictFind( tadd, "arguments" ), "paused", TRUE );
     1974                else {
     1975                    tr_benc * top = tr_new0( tr_benc, 1 );
     1976                    tr_bencInitDict( top, 2 );
     1977                    tr_bencDictAddStr( top, "method", "torrent-stop" );
     1978                    addIdArg( tr_bencDictAddDict( top, ARGUMENTS, 1 ), id );
     1979                    flush( host, port, &top );
     1980                }
     1981                break;
     1982            }
     1983            case 'w':
     1984            {
     1985                char * path = absolutify( optarg );
     1986                if( tadd )
     1987                    tr_bencDictAddStr( tr_bencDictFind( tadd, "arguments" ), "download-dir", path );
     1988                else {
     1989                    tr_benc * args = ensure_sset( &sset );
     1990                    tr_bencDictAddStr( args, "download-dir", path );
     1991                }
     1992                tr_free( path );
     1993                break;
     1994            }
     1995            case 963:
     1996            {
     1997                tr_benc * top = tr_new0( tr_benc, 1 );
     1998                tr_bencInitDict( top, 1 );
     1999                tr_bencDictAddStr( top, "method", "blocklist-update" );
     2000                flush( host, port, &top );
     2001                break;
     2002            }
     2003            case 921:
     2004            {
     2005                tr_benc * top = tr_new0( tr_benc, 1 );
     2006                tr_bencInitDict( top, 2 );
     2007                tr_bencDictAddStr( top, "method", "session-stats" );
     2008                tr_bencDictAddInt( top, "tag", TAG_STATS );
     2009                flush( host, port, &top );
     2010                break;
     2011            }
     2012            case 962:
     2013            {
     2014                tr_benc * top = tr_new0( tr_benc, 1 );
     2015                tr_bencInitDict( top, 2 );
     2016                tr_bencDictAddStr( top, "method", "port-test" );
     2017                tr_bencDictAddInt( top, "tag", TAG_PORTTEST );
     2018                flush( host, port, &top );
     2019                break;
     2020            }
     2021            case 'v':
     2022            {
     2023                tr_benc * top;
     2024                if( tset != 0 ) { addIdArg( tr_bencDictFind( tset, ARGUMENTS ), id ); flush( host, port, &tset ); }
     2025                top = tr_new0( tr_benc, 1 );
     2026                tr_bencInitDict( top, 2 );
     2027                tr_bencDictAddStr( top, "method", "torrent-verify" );
     2028                addIdArg( tr_bencDictAddDict( top, ARGUMENTS, 1 ), id );
     2029                flush( host, port, &top );
     2030                break;
     2031            }
     2032            case 'r':
     2033            case 'R':
     2034            {
     2035                tr_benc * args;
     2036                tr_benc * top = tr_new0( tr_benc, 1 );
     2037                tr_bencInitDict( top, 2 );
     2038                tr_bencDictAddStr( top, "method", "torrent-remove" );
     2039                args = tr_bencDictAddDict( top, ARGUMENTS, 2 );
     2040                tr_bencDictAddBool( args, "delete-local-data", c=='R' );
     2041                addIdArg( args, id );
     2042                flush( host, port, &top );
     2043                break;
     2044            }
     2045            case 960:
     2046            {
     2047                tr_benc * args;
     2048                tr_benc * top = tr_new0( tr_benc, 1 );
     2049                tr_bencInitDict( top, 2 );
     2050                tr_bencDictAddStr( top, "method", "torrent-set-location" );
     2051                args = tr_bencDictAddDict( top, ARGUMENTS, 3 );
     2052                tr_bencDictAddStr( args, "location", optarg );
     2053                tr_bencDictAddBool( args, "move", TRUE );
     2054                addIdArg( args, id );
     2055                flush( host, port, &top );
     2056                break;
     2057            }
     2058            default:
     2059            {
     2060                fprintf( stderr, "got opt [%d]\n", (int)c );
     2061                showUsage( );
     2062                break;
     2063            }
     2064        }
     2065    }
     2066
     2067    if( tadd != 0 ) flush( host, port, &tadd );
     2068    if( tset != 0 ) { addIdArg( tr_bencDictFind( tset, ARGUMENTS ), id ); flush( host, port, &tset ); }
     2069    if( sset != 0 ) flush( host, port, &sset );
    19762070    return status;
    19772071}
    19782072
     2073/* [host:port] or [host] or [port] */
     2074static void
     2075getHostAndPort( int * argc, char ** argv, char ** host, int * port )
     2076{
     2077    if( *argv[1] != '-' )
     2078    {
     2079        int          i;
     2080        const char * s = argv[1];
     2081        const char * delim = strchr( s, ':' );
     2082        if( delim )   /* user passed in both host and port */
     2083        {
     2084            *host = tr_strndup( s, delim - s );
     2085            *port = atoi( delim + 1 );
     2086        }
     2087        else
     2088        {
     2089            char *    end;
     2090            const int i = strtol( s, &end, 10 );
     2091            if( !*end ) /* user passed in a port */
     2092                *port = i;
     2093            else /* user passed in a host */
     2094                *host = tr_strdup( s );
     2095        }
     2096
     2097        *argc -= 1;
     2098        for( i = 1; i < *argc; ++i )
     2099            argv[i] = argv[i + 1];
     2100    }
     2101}
     2102
    19792103int
    1980 main( int     argc,
    1981       char ** argv )
    1982 {
    1983     int    i;
    1984     int    port = DEFAULT_PORT;
     2104main( int argc, char ** argv )
     2105{
     2106    int port = DEFAULT_PORT;
    19852107    char * host = NULL;
    1986     int    exit_status = EXIT_SUCCESS;
     2108    int exit_status = EXIT_SUCCESS;
    19872109
    19882110    if( argc < 2 ) {
     
    19952117        host = tr_strdup( DEFAULT_HOST );
    19962118
    1997     readargs( argc, (const char**)argv );
    1998     if( reqCount == 0 ) {
    1999         showUsage( );
    2000         return EXIT_FAILURE;
    2001     }
    2002 
    2003     exit_status = processRequests( host, port, (const char**)reqs, reqCount );
    2004 
    2005     for( i=0; i<reqCount; ++i )
    2006         tr_free( reqs[i] );
     2119    processArgs( host, port, argc, (const char**)argv );
    20072120
    20082121    tr_free( host );
    20092122    return exit_status;
    20102123}
    2011 
Note: See TracChangeset for help on using the changeset viewer.