source: trunk/utils/show.c @ 11382

Last change on this file since 11382 was 11382, checked in by charles, 11 years ago

(trunk libT) #3712 "transmission-show's file list should be sorted alphabetically" -- done.

  • Property svn:keywords set to Date Rev Author Id
File size: 8.8 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: show.c 11382 2010-11-03 16:59:24Z charles $
11 */
12
13#include <stdio.h>
14#include <time.h>
15
16#define CURL_DISABLE_TYPECHECK /* otherwise -Wunreachable-code goes insane */
17#include <curl/curl.h> 
18
19#include <event.h> /* struct evbuffer */
20
21#include <libtransmission/transmission.h>
22#include <libtransmission/bencode.h>
23#include <libtransmission/tr-getopt.h>
24#include <libtransmission/utils.h>
25#include <libtransmission/web.h> /* tr_webGetResponseStr() */
26#include <libtransmission/version.h>
27
28#define MY_NAME "transmission-show"
29#define TIMEOUT_SECS 30
30
31#define MEM_K 1024
32#define MEM_K_STR "KiB"
33#define MEM_M_STR "MiB"
34#define MEM_G_STR "GiB"
35#define MEM_T_STR "TiB"
36
37#define DISK_K 1024
38#define DISK_B_STR "B"
39#define DISK_K_STR "KiB"
40#define DISK_M_STR "MiB"
41#define DISK_G_STR "GiB"
42#define DISK_T_STR "TiB"
43
44#define SPEED_K 1024
45#define SPEED_B_STR "B/s"
46#define SPEED_K_STR "KiB/s"
47#define SPEED_M_STR "MiB/s"
48#define SPEED_G_STR "GiB/s"
49#define SPEED_T_STR "TiB/s"
50
51static tr_option options[] =
52{
53  { 's', "scrape", "Ask the torrent's trackers how many peers are in the torrent's swarm", "s", 0, NULL },
54  { 'V', "version", "Show version number and exit", "V", 0, NULL },
55  { 0, NULL, NULL, NULL, 0, NULL }
56};
57
58static const char *
59getUsage( void )
60{
61    return "Usage: " MY_NAME " [options] <.torrent file>";
62}
63
64static tr_bool scrapeFlag = FALSE;
65static tr_bool showVersion = FALSE;
66const char * filename = NULL;
67
68static int
69parseCommandLine( int argc, const char ** argv )
70{
71    int c;
72    const char * optarg;
73
74    while(( c = tr_getopt( getUsage( ), argc, argv, options, &optarg )))
75    {
76        switch( c )
77        {
78            case 's': scrapeFlag = TRUE; break;
79            case 'V': showVersion = TRUE; break;
80            case TR_OPT_UNK: filename = optarg; break;
81            default: return 1;
82        }
83    }
84
85    return 0;
86}
87
88static int
89compare_files_by_name( const void * va, const void * vb )
90{
91    const tr_file * a = *(const tr_file**)va;
92    const tr_file * b = *(const tr_file**)vb;
93    return strcmp( a->name, b->name );
94}
95
96static void
97showInfo( const tr_info * inf )
98{
99    int i;
100    char buf[128];
101    tr_file ** files;
102    int prevTier = -1;
103
104    /**
105    ***  General Info
106    **/
107
108    printf( "GENERAL\n\n" );
109    printf( "  Name: %s\n", inf->name );
110    printf( "  Hash: %s\n", inf->hashString );
111    printf( "  Created by: %s\n", inf->creator ? inf->creator : "Unknown" );
112    if( !inf->dateCreated )
113        printf( "  Created on: Unknown\n" );
114    else {
115        struct tm tm = *localtime( &inf->dateCreated );
116        printf( "  Created on: %s", asctime( &tm ) );
117    }
118    if( inf->comment && *inf->comment )
119        printf( "  Comment: %s\n", inf->comment );
120    printf( "  Piece Count: %d\n", inf->pieceCount );
121    printf( "  Piece Size: %s\n", tr_formatter_mem_B( buf, inf->pieceSize, sizeof( buf ) ) );
122    printf( "  Total Size: %s\n", tr_formatter_size_B( buf, inf->totalSize, sizeof( buf ) ) );
123    printf( "  Privacy: %s\n", inf->isPrivate ? "Private torrent" : "Public torrent" );
124
125    /**
126    ***  Trackers
127    **/
128
129    printf( "\nTRACKERS\n" );
130    for( i=0; i<(int)inf->trackerCount; ++i ) 
131    {
132        if( prevTier != inf->trackers[i].tier )
133        {
134            prevTier = inf->trackers[i].tier;
135            printf( "\n  Tier #%d\n", prevTier + 1 );
136        }
137
138        printf( "  %s\n", inf->trackers[i].announce );
139    }
140
141    /**
142    ***  Files
143    **/
144
145    printf( "\nFILES\n\n" );
146    files = tr_new( tr_file*, inf->fileCount );
147    for( i=0; i<(int)inf->fileCount; ++i )
148        files[i] = &inf->files[i];
149    qsort( files, inf->fileCount, sizeof(tr_file*), compare_files_by_name );
150    for( i=0; i<(int)inf->fileCount; ++i )
151        printf( "  %s (%s)\n", files[i]->name, tr_formatter_size_B( buf, files[i]->length, sizeof( buf ) ) );
152    tr_free( files );
153}
154
155static size_t
156writeFunc( void * ptr, size_t size, size_t nmemb, void * buf )
157{
158    const size_t byteCount = size * nmemb;
159    evbuffer_add( buf, ptr, byteCount );
160    return byteCount;
161}
162
163static CURL*
164tr_curl_easy_init( struct evbuffer * writebuf )
165{
166    CURL * curl = curl_easy_init( );
167    curl_easy_setopt( curl, CURLOPT_USERAGENT, MY_NAME "/" LONG_VERSION_STRING );
168    curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, writeFunc );
169    curl_easy_setopt( curl, CURLOPT_WRITEDATA, writebuf );
170    curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
171    curl_easy_setopt( curl, CURLOPT_VERBOSE, getenv( "TR_CURL_VERBOSE" ) != NULL );
172    curl_easy_setopt( curl, CURLOPT_ENCODING, "" );
173    return curl;
174}
175
176static void
177doScrape( const tr_info * inf )
178{
179    int i;
180
181    for( i=0; i<inf->trackerCount; ++i )
182    {
183        CURL * curl;
184        CURLcode res;
185        struct evbuffer * buf;
186        const char * scrape = inf->trackers[i].scrape;
187        char * url;
188
189        if( scrape == NULL )
190            continue;
191
192        url = tr_strdup_printf( "%s%cinfo_hash=%s",
193                                scrape,
194                                strchr( scrape, '?' ) ? '&' : '?',
195                                inf->hashEscaped );
196
197        printf( "%s ... ", url );
198        fflush( stdout );
199
200        buf = evbuffer_new( );
201        curl = tr_curl_easy_init( buf );
202        curl_easy_setopt( curl, CURLOPT_URL, url );
203        curl_easy_setopt( curl, CURLOPT_TIMEOUT, TIMEOUT_SECS );
204
205        if(( res = curl_easy_perform( curl )))
206        {
207            printf( "error: %s\n", curl_easy_strerror( res ) );
208        }
209        else
210        {
211            long response;
212            curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &response );
213            if( response != 200 )
214            {
215                printf( "error: unexpected response %ld \"%s\"\n",
216                        response,
217                        tr_webGetResponseStr( response ) );
218            }
219            else /* HTTP OK */
220            {
221                tr_benc top;
222                tr_benc * files;
223                tr_bool matched = FALSE;
224                const char * begin = (const char*) EVBUFFER_DATA( buf );
225                const char * end = begin + EVBUFFER_LENGTH( buf );
226
227                if( !tr_bencParse( begin, end, &top, NULL ) )
228                {
229                    if( tr_bencDictFindDict( &top, "files", &files ) )
230                    {
231                        int i = 0;
232                        tr_benc * val;
233                        const char * key;
234
235                        while( tr_bencDictChild( files, i++, &key, &val ))
236                        {
237                            if( !memcmp( inf->hash, key, SHA_DIGEST_LENGTH ) )
238                            {
239                                int64_t seeders = -1;
240                                int64_t leechers = -1;
241                                tr_bencDictFindInt( val, "complete", &seeders );
242                                tr_bencDictFindInt( val, "incomplete", &leechers );
243                                printf( "%d seeders, %d leechers\n", (int)seeders, (int)leechers );
244                                matched = TRUE;
245                            }
246                        }
247                    }
248
249                    tr_bencFree( &top );
250                }
251
252                if( !matched )
253                    printf( "no match\n" );
254            }
255        }
256
257        curl_easy_cleanup( curl );
258        evbuffer_free( buf );
259        tr_free( url );
260    }
261}
262
263int
264main( int argc, char * argv[] )
265{
266    int err;
267    tr_info inf;
268    tr_ctor * ctor;
269
270    tr_setMessageLevel( TR_MSG_ERR );
271    tr_formatter_mem_init  ( MEM_K, MEM_K_STR, MEM_M_STR, MEM_G_STR, MEM_T_STR );
272    tr_formatter_size_init ( DISK_K, DISK_K_STR, DISK_M_STR, DISK_G_STR, DISK_T_STR );
273    tr_formatter_speed_init ( SPEED_K, SPEED_K_STR, SPEED_M_STR, SPEED_G_STR, SPEED_T_STR );
274
275    if( parseCommandLine( argc, (const char**)argv ) )
276        return EXIT_FAILURE;
277
278    if( showVersion )
279    {
280        fprintf( stderr, MY_NAME" "LONG_VERSION_STRING"\n" );
281        return 0;
282    }
283
284    /* make sure the user specified a filename */
285    if( !filename )
286    {
287        fprintf( stderr, "ERROR: No .torrent file specified.\n" );
288        tr_getopt_usage( MY_NAME, getUsage( ), options );
289        fprintf( stderr, "\n" );
290        return EXIT_FAILURE;
291    }
292
293    /* try to parse the .torrent file */
294    ctor = tr_ctorNew( NULL );
295    tr_ctorSetMetainfoFromFile( ctor, filename );
296    err = tr_torrentParse( ctor, &inf );
297    tr_ctorFree( ctor );
298    if( err )
299    {
300        fprintf( stderr, "Error parsing .torrent file \"%s\"\n", filename );
301        return 1;
302    }
303
304    printf( "Name: %s\n", inf.name );
305    printf( "File: %s\n", filename );
306    printf( "\n" );
307    fflush( stdout );
308
309    if( scrapeFlag )
310        doScrape( &inf );
311    else
312        showInfo( &inf );
313
314    /* cleanup */
315    putc( '\n', stdout );
316    tr_metainfoFree( &inf );
317    return 0;
318}
Note: See TracBrowser for help on using the repository browser.