Changeset 10783


Ignore:
Timestamp:
Jun 16, 2010, 2:27:24 PM (10 years ago)
Author:
charles
Message:

(trunk) #2983: add command-line utilities for creating .torrent files, for editing passkeys, for adding/removing trackers, etc.

Location:
trunk
Files:
7 added
11 edited

Legend:

Unmodified
Added
Removed
  • trunk/Makefile.am

    r9794 r10783  
    2020  third-party \
    2121  libtransmission \
     22  utils \
    2223  $(DAEMON_DIR) \
    2324  $(CLI_DIR) \
  • trunk/cli/cli.c

    r10640 r10783  
    3131#include <libtransmission/transmission.h>
    3232#include <libtransmission/bencode.h>
    33 #include <libtransmission/makemeta.h>
    3433#include <libtransmission/tr-getopt.h>
    3534#include <libtransmission/utils.h> /* tr_wait_msec */
     
    4039#define MY_NAME "transmissioncli"
    4140
    42 static tr_bool showInfo         = 0;
    43 static tr_bool showScrape       = 0;
    44 static tr_bool isPrivate        = 0;
    4541static tr_bool verify           = 0;
    4642static sig_atomic_t gotsig           = 0;
     
    4844
    4945static const char * torrentPath  = NULL;
    50 static const char * sourceFile   = NULL;
    51 static const char * comment      = NULL;
    52 
    53 #define MAX_ANNOUNCE 128
    54 static tr_tracker_info announce[MAX_ANNOUNCE];
    55 static int announceCount = 0;
    5646
    5747static const struct tr_option options[] =
    5848{
    59     { 'a', "announce",             "Set the new torrent's announce URL", "a",  1, "<url>"     },
    6049    { 'b', "blocklist",            "Enable peer blocklists", "b",  0, NULL        },
    6150    { 'B', "no-blocklist",         "Disable peer blocklists", "B",  0, NULL        },
    62     { 'c', "comment",              "Set the new torrent's comment", "c",  1, "<comment>" },
    6351    { 'd', "downlimit",            "Set max download speed in KiB/s", "d",  1, "<speed>"   },
    6452    { 'D', "no-downlimit",         "Don't limit the download speed", "D",  0, NULL        },
     
    6856    { 'f', "finish",               "Run a script when the torrent finishes", "f", 1, "<script>" },
    6957    { 'g', "config-dir",           "Where to find configuration files", "g", 1, "<path>" },
    70     { 'i', "info",                 "Show torrent details and exit", "i",  0, NULL        },
    7158    { 'm', "portmap",              "Enable portmapping via NAT-PMP or UPnP", "m",  0, NULL        },
    7259    { 'M', "no-portmap",           "Disable portmapping", "M",  0, NULL        },
    73     { 'n', "new",                  "Create a new torrent", "n", 1, "<source>" },
    7460    { 'p', "port", "Port for incoming peers (Default: " TR_DEFAULT_PEER_PORT_STR ")", "p", 1, "<port>" },
    75     { 'r', "private",              "Set the new torrent's 'private' flag", "r",  0, NULL        },
    76     { 's', "scrape",               "Scrape the torrent and exit", "s",  0, NULL        },
    7761    { 't', "tos", "Peer socket TOS (0 to 255, default=" TR_DEFAULT_PEER_SOCKET_TOS_STR ")", "t", 1, "<tos>" },
    7862    { 'u', "uplimit",              "Set max upload speed in KiB/s", "u",  1, "<speed>"   },
     
    11397}
    11498
    115 static int
    116 is_rfc2396_alnum( char ch )
    117 {
    118     return ( '0' <= ch && ch <= '9' )
    119            || ( 'A' <= ch && ch <= 'Z' )
    120            || ( 'a' <= ch && ch <= 'z' );
    121 }
    122 
    123 static void
    124 escape( char *          out,
    125         const uint8_t * in,
    126         int             in_len )                     /* rfc2396 */
    127 {
    128     const uint8_t *end = in + in_len;
    129 
    130     while( in != end )
    131         if( is_rfc2396_alnum( *in ) )
    132             *out++ = (char) *in++;
    133         else
    134             out += tr_snprintf( out, 4, "%%%02X", (unsigned int)*in++ );
    135 
    136     *out = '\0';
    137 }
    138 
    13999static tr_bool waitingOnWeb;
    140100
     
    148108    tr_ctorSetMetainfo( ctor, response, response_byte_count );
    149109    waitingOnWeb = FALSE;
    150 }
    151 
    152 static int leftToScrape = 0;
    153 
    154 static void
    155 scrapeDoneFunc( tr_session   * session UNUSED,
    156                 long           response_code,
    157                 const void   * response,
    158                 size_t         response_byte_count,
    159                 void         * host )
    160 {
    161     tr_benc top, *files;
    162 
    163     if( !tr_bencLoad( response, response_byte_count, &top, NULL )
    164       && tr_bencDictFindDict( &top, "files", &files )
    165       && files->val.l.count >= 2 )
    166     {
    167         int64_t   complete = -1, incomplete = -1, downloaded = -1;
    168         tr_benc * hash = &files->val.l.vals[1];
    169         tr_bencDictFindInt( hash, "complete", &complete );
    170         tr_bencDictFindInt( hash, "incomplete", &incomplete );
    171         tr_bencDictFindInt( hash, "downloaded", &downloaded );
    172         printf( "%4d seeders, %4d leechers, %5d downloads at %s\n",
    173                 (int)complete, (int)incomplete, (int)downloaded,
    174                 (char*)host );
    175         tr_bencFree( &top );
    176     }
    177     else
    178         fprintf( stderr, "Unable to parse response (http code %lu) at %s",
    179                  response_code,
    180                  (char*)host );
    181 
    182     --leftToScrape;
    183 
    184     tr_free( host );
    185 }
    186 
    187 static void
    188 dumpInfo( FILE *          out,
    189           const tr_info * inf )
    190 {
    191     int             i;
    192     int             prevTier = -1;
    193     tr_file_index_t ff;
    194 
    195     fprintf( out, "hash:\t" );
    196     for( i = 0; i < SHA_DIGEST_LENGTH; ++i )
    197         fprintf( out, "%02x", inf->hash[i] );
    198     fprintf( out, "\n" );
    199 
    200     fprintf( out, "name:\t%s\n", inf->name );
    201 
    202     for( i = 0; i < inf->trackerCount; ++i )
    203     {
    204         if( prevTier != inf->trackers[i].tier )
    205         {
    206             prevTier = inf->trackers[i].tier;
    207             fprintf( out, "\ntracker tier #%d:\n", ( prevTier + 1 ) );
    208         }
    209         fprintf( out, "\tannounce:\t%s\n", inf->trackers[i].announce );
    210     }
    211 
    212     fprintf( out, "size:\t%" PRIu64 " (%" PRIu64 " * %d + %" PRIu64 ")\n",
    213              inf->totalSize, inf->totalSize / inf->pieceSize,
    214              inf->pieceSize, inf->totalSize % inf->pieceSize );
    215 
    216     if( inf->comment && *inf->comment )
    217         fprintf( out, "comment:\t%s\n", inf->comment );
    218     if( inf->creator && *inf->creator )
    219         fprintf( out, "creator:\t%s\n", inf->creator );
    220     if( inf->isPrivate )
    221         fprintf( out, "private flag set\n" );
    222 
    223     fprintf( out, "file(s):\n" );
    224     for( ff = 0; ff < inf->fileCount; ++ff )
    225         fprintf( out, "\t%s (%" PRIu64 ")\n", inf->files[ff].name,
    226                  inf->files[ff].length );
    227110}
    228111
     
    305188    tr_benc       settings;
    306189    const char  * configDir;
    307     tr_bool       haveSource;
    308     tr_bool       haveAnnounce;
    309190    uint8_t     * fileContents;
    310191    size_t        fileLength;
     
    334215    }
    335216
    336     /* don't bind the port if we're just running the CLI
    337        to get metainfo or to create a torrent */
    338     if( showInfo || showScrape || ( sourceFile != NULL ) )
    339         tr_bencDictAddInt( &settings, TR_PREFS_KEY_PEER_PORT, -1 );
    340 
    341217    h = tr_sessionInit( "cli", configDir, FALSE, &settings );
    342218
    343     haveSource = sourceFile && *sourceFile;
    344     haveAnnounce = announceCount > 0;
    345 
    346     if( haveSource && !haveAnnounce )
    347         fprintf( stderr, "Did you mean to create a torrent without a tracker's announce URL?\n" );
    348 
    349     if( haveSource ) /* creating a torrent */
    350     {
    351         int err;
    352         tr_metainfo_builder * b;
    353         fprintf( stderr, "creating torrent \"%s\"\n", torrentPath );
    354 
    355         b = tr_metaInfoBuilderCreate( sourceFile );
    356         tr_makeMetaInfo( b, torrentPath, announce, announceCount, comment, isPrivate );
    357         while( !b->isDone )
    358         {
    359             tr_wait_msec( 1000 );
    360             printf( "." );
    361         }
    362 
    363         err = b->result;
    364         tr_metaInfoBuilderFree( b );
    365         return err;
    366     }
    367 
    368219    ctor = tr_ctorNew( h );
    369220
    370221    fileContents = tr_loadFile( torrentPath, &fileLength );
    371     tr_ctorSetPaused( ctor, TR_FORCE, showScrape );
     222    tr_ctorSetPaused( ctor, TR_FORCE, FALSE );
    372223    if( fileContents != NULL ) {
    373224        tr_ctorSetMetainfo( ctor, fileContents, fileLength );
     
    380231    }
    381232    tr_free( fileContents );
    382 
    383     if( showScrape )
    384     {
    385         tr_info info;
    386 
    387         if( !tr_torrentParse( ctor, &info ) )
    388         {
    389             int          i;
    390             const time_t start = time( NULL );
    391             for( i = 0; i < info.trackerCount; ++i )
    392             {
    393                 if( info.trackers[i].scrape )
    394                 {
    395                     const char * scrape = info.trackers[i].scrape;
    396                     char         escaped[SHA_DIGEST_LENGTH * 3 + 1];
    397                     char *       url, *host;
    398                     escape( escaped, info.hash, SHA_DIGEST_LENGTH );
    399                     url = tr_strdup_printf( "%s%cinfo_hash=%s",
    400                                             scrape,
    401                                             strchr( scrape,
    402                                                     '?' ) ? '&' : '?',
    403                                             escaped );
    404                     tr_urlParse( scrape, -1, NULL, &host, NULL, NULL );
    405                     ++leftToScrape;
    406 
    407                     tr_webRun( h, url, NULL, scrapeDoneFunc, host );
    408                     tr_free( url );
    409                 }
    410             }
    411 
    412             fprintf( stderr, "scraping %d trackers:\n", leftToScrape );
    413 
    414             while( leftToScrape > 0 && ( ( time( NULL ) - start ) < 20 ) )
    415                 tr_wait_msec( 250 );
    416         }
    417         goto cleanup;
    418     }
    419 
    420     if( showInfo )
    421     {
    422         tr_info info;
    423 
    424         if( !tr_torrentParse( ctor, &info ) )
    425         {
    426             dumpInfo( stdout, &info );
    427             tr_metainfoFree( &info );
    428         }
    429 
    430         tr_ctorFree( ctor );
    431         goto cleanup;
    432     }
    433233
    434234    tor = tr_torrentNew( ctor, &error );
     
    496296    }
    497297
    498 cleanup:
    499 
    500298    tr_sessionSaveSettings( h, configDir, &settings );
    501299
     
    522320        switch( c )
    523321        {
    524             case 'a': if( announceCount + 1 < MAX_ANNOUNCE ) {
    525                           announce[announceCount].tier = announceCount;
    526                           announce[announceCount].announce = (char*) optarg;
    527                           ++announceCount;
    528                       }
    529                       break;
    530322            case 'b': tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, TRUE );
    531323                      break;
    532324            case 'B': tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED, FALSE );
    533                       break;
    534             case 'c': comment = optarg;
    535325                      break;
    536326            case 'd': tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED, atoi( optarg ) );
     
    544334            case 'g': /* handled above */
    545335                      break;
    546             case 'i': showInfo = 1;
    547                       break;
    548336            case 'm': tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, TRUE );
    549337                      break;
    550338            case 'M': tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING, FALSE );
    551339                      break;
    552             case 'n': sourceFile = optarg; break;
    553340            case 'p': tr_bencDictAddInt( d, TR_PREFS_KEY_PEER_PORT, atoi( optarg ) );
    554                       break;
    555             case 'r': isPrivate = 1;
    556                       break;
    557             case 's': showScrape = 1;
    558341                      break;
    559342            case 't': tr_bencDictAddInt( d, TR_PREFS_KEY_PEER_SOCKET_TOS, atoi( optarg ) );
  • trunk/cli/transmissioncli.1

    r9122 r10783  
    2525.Fl h
    2626.Nm
    27 .Fl i
    28 .Ar torrent-file
    29 .Nm
    30 .Fl s
    31 .Ar torrent-file
    32 .Nm
    33 .Fl n Ar sourcefile
    34 .Op Fl a Ar url
    35 .Op Fl c Ar comment
    36 .Op Fl r
    37 .Ar new-torrent-file
    38 .Nm
    3927.Op Fl b | B
    4028.Op Fl d Ar number | Fl D
     
    5947The options are as follows:
    6048.Bl -tag -width Ds
    61 .It Fl a, Fl -announce Ar announce-url
    62 When creating a new torrent, set its announce URL
    6349.It Fl b Fl -blocklist
    6450Enable peer blocklists.  Transmission understands the bluetack blocklist file format.
     
    6652.It Fl B Fl -no-blocklist
    6753Disble blocklists.
    68 .It Fl c, Fl -comment Ar comment-text
    69 When creating a new torrent, set its comment field
    7054.It Fl d, -downlimit Ar number
    7155Set the maximum download speed in KB/s
     
    8569.It Fl h, Fl -help
    8670Prints a short usage summary.
    87 .It Fl i, Fl -info
    88 Shows torrent details and exit
    8971.It Fl m, Fl -portmap
    9072Enable portmapping via NAT-PMP or UPnP
     
    9577.It Fl p, -port Ar port
    9678Set the port to listen for incoming peers. (Default: 51413)
    97 .It Fl r, Fl -private
    98 When creating a new torrent, set its 'private' flag
    99 .It Fl s, -scrape
    100 Print the current number of seeders and leechers for the specified torrent
    10179.It Fl t, -tos
    10280Set the peer socket TOS for local router-based traffic shaping.
  • trunk/configure.ac

    r10767 r10783  
    477477                 doc/Makefile
    478478                 libtransmission/Makefile
     479                 utils/Makefile
    479480                 third-party/Makefile
    480481                 third-party/miniupnp/Makefile
  • trunk/daemon/daemon.c

    r10782 r10783  
    22 * This file Copyright (C) 2008-2010 Mnemosyne LLC
    33 *
    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.
     4 * This program is free software; you can redistribute it and/or modify
     5 * it under the terms of the GNU General Public License version 2
     6 * as published by the Free Software Foundation.
     7 *
     8 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
    99 *
    1010 * $Id$
  • trunk/daemon/remote.c

    r10782 r10783  
    22 * This file Copyright (C) 2008-2010 Mnemosyne LLC
    33 *
    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.
     4 * This program is free software; you can redistribute it and/or modify
     5 * it under the terms of the GNU General Public License version 2
     6 * as published by the Free Software Foundation.
     7 *
     8 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
    99 *
    1010 * $Id$
  • trunk/daemon/watch.c

    r10742 r10783  
    22 * This file Copyright (C) 2009-2010 Mnemosyne LLC
    33 *
    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.
     4 * This program is free software; you can redistribute it and/or modify
     5 * it under the terms of the GNU General Public License version 2
     6 * as published by the Free Software Foundation.
     7 *
     8 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
    99 *
    1010 * $Id$
  • trunk/libtransmission/bencode.c

    r10634 r10783  
    422422        ret = val->val.l.vals + i;
    423423    return ret;
     424}
     425
     426int
     427tr_bencListRemove( tr_benc * list, size_t i )
     428{
     429    if( tr_bencIsList( list ) && ( i < list->val.l.count ) )
     430    {
     431        tr_bencFree( &list->val.l.vals[i] );
     432        tr_removeElementFromArray( list->val.l.vals, i, sizeof( tr_benc ), list->val.l.count-- );
     433        return 1;
     434    }
     435
     436    return 0;
    424437}
    425438
  • trunk/libtransmission/bencode.h

    r10084 r10783  
    157157tr_benc * tr_bencListChild( tr_benc * list, size_t n );
    158158
     159int       tr_bencListRemove( tr_benc *, size_t n );
     160
    159161/***
    160162****
  • trunk/libtransmission/list.h

    r10524 r10783  
    1212
    1313#ifndef __TRANSMISSION__
    14 #error only libtransmission should #include this header.
     14 #error only libtransmission should #include this header.
    1515#endif
    1616
  • trunk/libtransmission/utils.c

    r10736 r10783  
    115115tr_setMessageLevel( int level )
    116116{
    117     tr_lockLock( messageLock );
    118 
    119117    messageLevel = MAX( 0, level );
    120 
    121     tr_lockUnlock( messageLock );
    122118}
    123119
Note: See TracChangeset for help on using the changeset viewer.