source: trunk/utils/edit.c @ 10783

Last change on this file since 10783 was 10783, checked in by charles, 12 years ago

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

  • Property svn:keywords set to Date Rev Author Id
File size: 8.7 KB
Line 
1/*
2 * This file Copyright (C) 2010 Mnemosyne LLC
3 *
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
9 *
10 * $Id: edit.c 10783 2010-06-16 14:27:24Z charles $
11 */
12
13#include <stdio.h>
14
15#include <event.h> /* evbuffer */
16
17#include <libtransmission/transmission.h>
18#include <libtransmission/bencode.h>
19#include <libtransmission/tr-getopt.h>
20#include <libtransmission/utils.h>
21
22#define MY_NAME "transmission-edit"
23
24int fileCount = 0;
25const char ** files = NULL;
26const char * add = NULL;
27const char * deleteme = NULL;
28const char * replace[2] = { NULL, NULL };
29
30static tr_option options[] =
31{
32  { 'a', "add", "Add a tracker's announce URL", "a", 1, "<url>" },
33  { 'd', "delete", "Delete a tracker's announce URL", "d", 1, "<url>" },
34  { 'r', "replace", "Search and replace a substring in the announce URLs", "r", 1, "<old> <new>" },
35  { 0, NULL, NULL, NULL, 0, NULL }
36};
37
38static const char *
39getUsage( void )
40{
41    return "Usage: " MY_NAME " [options] torrent-file(s)";
42}
43
44static int
45parseCommandLine( int argc, const char ** argv )
46{
47    int c;
48    const char * optarg;
49
50    while(( c = tr_getopt( getUsage( ), argc, argv, options, &optarg )))
51    {
52        switch( c )
53        {
54            case 'a': add = optarg;
55                      break;
56            case 'd': deleteme = optarg;
57                      break;
58            case 'r': replace[0] = optarg;
59                      c = tr_getopt( getUsage( ), argc, argv, options, &optarg );
60                      if( c != TR_OPT_UNK ) return 1;
61                      replace[1] = optarg;
62                      break;
63            case TR_OPT_UNK: files[fileCount++] = optarg; break;
64            default: return 1;
65        }
66    }
67
68    return 0;
69}
70
71static tr_bool
72removeURL( tr_benc * metainfo, const char * url )
73{
74    const char * str;
75    tr_benc * announce_list;
76    tr_bool changed = FALSE;
77
78    if( tr_bencDictFindStr( metainfo, "announce", &str ) && !strcmp( str, url ) )
79    {
80        printf( "\tRemoved \"%s\" from \"announce\"\n", str );
81        tr_bencDictRemove( metainfo, "announce" );
82        changed = TRUE;
83    }
84
85    if( tr_bencDictFindList( metainfo, "announce-list", &announce_list ) )
86    {
87        tr_benc * tier;
88        int tierIndex = 0;
89        while(( tier = tr_bencListChild( announce_list, tierIndex )))
90        {
91            tr_benc * node;
92            int nodeIndex = 0;
93            while(( node = tr_bencListChild( tier, nodeIndex )))
94            {
95                if( tr_bencGetStr( node, &str ) && !strcmp( str, url ) )
96                {
97                    printf( "\tRemoved \"%s\" from \"announce-list\" tier #%d\n", str, (tierIndex+1) );
98                    tr_bencListRemove( tier, nodeIndex );
99                    changed = TRUE;
100                }
101                else ++nodeIndex;
102            }
103
104            if( tr_bencListSize( tier ) == 0 )
105            {
106                printf( "\tNo URLs left in tier #%d... removing tier\n", (tierIndex+1) );
107                tr_bencListRemove( announce_list, tierIndex );
108            }
109            else ++tierIndex;
110        }
111
112        if( tr_bencListSize( announce_list ) == 0 )
113        {
114            printf( "\tNo tiers left... removing announce-list\n" );
115            tr_bencDictRemove( metainfo, "announce-list" );
116        }
117    }
118
119    /* if we removed the "announce" field and there's still another track left,
120     * use it as the "announce" field */
121    if( changed && !tr_bencDictFindStr( metainfo, "announce", &str ) )
122    {
123        tr_benc * tier;
124        tr_benc * node;
125
126        if(( tier = tr_bencListChild( announce_list, 0 ))) {
127            if(( node = tr_bencListChild( tier, 0 ))) {
128                if( tr_bencGetStr( node, &str ) ) {
129                    tr_bencDictAddStr( metainfo, "announce", str );
130                    printf( "\tAdded \"%s\" to announce\n", str );
131                }
132            }
133        }
134    }
135       
136
137    return changed;
138}
139
140static char*
141replaceSubstr( const char * str, const char * in, const char * out )
142{
143    char * walk;
144    struct evbuffer * buf = evbuffer_new( );
145    const size_t inlen = strlen( in );
146    const size_t outlen = strlen( out );
147
148    while(( walk = strstr( str, in )))
149    {
150        evbuffer_add( buf, str, walk-str );
151        evbuffer_add( buf, out, outlen );
152        str = walk + inlen;
153    }
154
155    walk = tr_strndup( EVBUFFER_DATA( buf ), EVBUFFER_LENGTH( buf ) );
156    evbuffer_free( buf );
157    return walk;
158}
159
160static tr_bool
161replaceURL( tr_benc * metainfo, const char * in, const char * out )
162{
163    const char * str;
164    tr_benc * announce_list;
165    tr_bool changed = FALSE;
166
167    if( tr_bencDictFindStr( metainfo, "announce", &str ) && strstr( str, in ) )
168    {
169        char * newstr = replaceSubstr( str, in, out );
170        printf( "\tReplaced in \"announce\": \"%s\" --> \"%s\"\n", str, newstr );
171        tr_bencDictAddStr( metainfo, "announce", newstr );
172        tr_free( newstr );
173        changed = TRUE;
174    }
175
176    if( tr_bencDictFindList( metainfo, "announce-list", &announce_list ) )
177    {
178        tr_benc * tier;
179        int tierCount = 0;
180        while(( tier = tr_bencListChild( announce_list, tierCount++ )))
181        {
182            tr_benc * node;
183            int nodeCount = 0;
184            while(( node = tr_bencListChild( tier, nodeCount++ )))
185            {
186                if( tr_bencGetStr( node, &str ) && strstr( str, in ) )
187                {
188                    char * newstr = replaceSubstr( str, in, out );
189                    printf( "\tReplaced in \"announce-list\" tier %d: \"%s\" --> \"%s\"\n", tierCount, str, newstr );
190                    tr_bencFree( node );
191                    tr_bencInitStr( node, newstr, -1 );
192                    tr_free( newstr );
193                    changed = TRUE;
194                }
195            }
196        }
197    }
198
199    return changed;
200}
201
202static tr_bool
203addURL( tr_benc * metainfo, const char * url )
204{
205    const char * str;
206    tr_benc * announce_list;
207    tr_bool changed = FALSE;
208    tr_bool match = FALSE;
209
210    /* maybe add it to "announce" */
211    if( !tr_bencDictFindStr( metainfo, "announce", &str ) )
212    {
213        printf( "\tAdded \"%s\" in \"announce\"\n", url );
214        tr_bencDictAddStr( metainfo, "announce", url );
215        changed = TRUE;
216    }
217
218    /* see if it's already in announce-list */
219    if( tr_bencDictFindList( metainfo, "announce-list", &announce_list ) ) {
220        tr_benc * tier;
221        int tierCount = 0;
222        while(( tier = tr_bencListChild( announce_list, tierCount++ ))) {
223            tr_benc * node;
224            int nodeCount = 0;
225            while(( node = tr_bencListChild( tier, nodeCount++ )))
226                if( tr_bencGetStr( node, &str ) && !strcmp( str, url ) )
227                    match = TRUE;
228        }
229    }
230
231    /* if it's not in announce-list, add it now */
232    if( !match )
233    {
234        tr_benc * tier;
235
236        if( !tr_bencDictFindList( metainfo, "announce-list", &announce_list ) )
237            announce_list = tr_bencDictAddList( metainfo, "announce-list", 1 );
238
239        tier = tr_bencListAddList( announce_list, 1 );
240        tr_bencListAddStr( tier, url );
241        printf( "\tAdded \"%s\" to \"announce-list\" tier %d\n", url, tr_bencListSize( announce_list ) );
242        changed = TRUE;
243    }
244
245    return changed;
246}
247
248int
249main( int argc, char * argv[] )
250{
251    int i;
252    int changedCount = 0;
253
254    files = tr_new0( const char*, argc );
255
256    tr_setMessageLevel( TR_MSG_ERR );
257
258    if( parseCommandLine( argc, (const char**)argv ) )
259        return EXIT_FAILURE;
260
261    if( fileCount < 1 )
262    {
263        fprintf( stderr, "ERROR: No torrent files specified.\n" );
264        tr_getopt_usage( MY_NAME, getUsage( ), options );
265        fprintf( stderr, "\n" );
266        return EXIT_FAILURE;
267    }
268
269    if( !add && !deleteme && !replace[0] )
270    {
271        fprintf( stderr, "ERROR: Must specify -a, -d or -r\n" );
272        tr_getopt_usage( MY_NAME, getUsage( ), options );
273        fprintf( stderr, "\n" );
274        return EXIT_FAILURE;
275    }
276
277    for( i=0; i<fileCount; ++i )
278    {
279        tr_benc top;
280        tr_bool changed = FALSE;
281        const char * filename = files[i];
282
283        printf( "%s\n", filename );
284
285        if( tr_bencLoadFile( &top, TR_FMT_BENC, filename ) )
286        {
287            printf( "\tError reading file\n" );
288            continue;
289        }
290
291        if( deleteme != NULL )
292            changed |= removeURL( &top, deleteme );
293
294        if( add != NULL )
295            changed = addURL( &top, add );
296
297        if( replace[0] && replace[1] )
298            changed |= replaceURL( &top, replace[0], replace[1] );
299
300        if( changed )
301        {
302            ++changedCount;
303            tr_bencToFile( &top, TR_FMT_BENC, filename );
304        }
305
306        tr_bencFree( &top );
307    }
308
309    printf( "Changed %d files\n", changedCount );
310
311    tr_free( files );
312    return 0;
313}
Note: See TracBrowser for help on using the repository browser.