source: trunk/utils/show.c @ 11263

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

(trunk) #3577 -- replace gear icon for GTK+, Qt clients

  • Property svn:keywords set to Date Rev Author Id
File size: 8.4 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 11263 2010-09-24 15:22:53Z 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 void
89showInfo( const tr_info * inf )
90{
91    int i;
92    char buf[128];
93    int prevTier = -1;
94
95    /**
96    ***  General Info
97    **/
98
99    printf( "GENERAL\n\n" );
100    printf( "  Name: %s\n", inf->name );
101    printf( "  Hash: %s\n", inf->hashString );
102    printf( "  Created by: %s\n", inf->creator ? inf->creator : "Unknown" );
103    if( !inf->dateCreated )
104        printf( "  Created on: Unknown\n" );
105    else {
106        struct tm tm = *localtime( &inf->dateCreated );
107        printf( "  Created on: %s", asctime( &tm ) );
108    }
109    if( inf->comment && *inf->comment )
110        printf( "  Comment: %s\n", inf->comment );
111    printf( "  Piece Count: %d\n", inf->pieceCount );
112    printf( "  Piece Size: %s\n", tr_formatter_mem_B( buf, inf->pieceSize, sizeof( buf ) ) );
113    printf( "  Total Size: %s\n", tr_formatter_size_B( buf, inf->totalSize, sizeof( buf ) ) );
114    printf( "  Privacy: %s\n", inf->isPrivate ? "Private torrent" : "Public torrent" );
115
116    /**
117    ***  Trackers
118    **/
119
120    printf( "\nTRACKERS\n" );
121    for( i=0; i<(int)inf->trackerCount; ++i ) 
122    {
123        if( prevTier != inf->trackers[i].tier )
124        {
125            prevTier = inf->trackers[i].tier;
126            printf( "\n  Tier #%d\n", prevTier + 1 );
127        }
128
129        printf( "  %s\n", inf->trackers[i].announce );
130    }
131
132    /**
133    ***  Files
134    **/
135
136    printf( "\nFILES\n\n" );
137    for( i=0; i<(int)inf->fileCount; ++i )
138        printf( "  %s (%s)\n", inf->files[i].name, tr_formatter_size_B( buf, inf->files[i].length, sizeof( buf ) ) );
139}
140
141static size_t
142writeFunc( void * ptr, size_t size, size_t nmemb, void * buf )
143{
144    const size_t byteCount = size * nmemb;
145    evbuffer_add( buf, ptr, byteCount );
146    return byteCount;
147}
148
149static CURL*
150tr_curl_easy_init( struct evbuffer * writebuf )
151{
152    CURL * curl = curl_easy_init( );
153    curl_easy_setopt( curl, CURLOPT_USERAGENT, MY_NAME "/" LONG_VERSION_STRING );
154    curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, writeFunc );
155    curl_easy_setopt( curl, CURLOPT_WRITEDATA, writebuf );
156    curl_easy_setopt( curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY );
157    curl_easy_setopt( curl, CURLOPT_VERBOSE, getenv( "TR_CURL_VERBOSE" ) != NULL );
158    curl_easy_setopt( curl, CURLOPT_ENCODING, "" );
159    return curl;
160}
161
162static void
163doScrape( const tr_info * inf )
164{
165    int i;
166
167    for( i=0; i<inf->trackerCount; ++i )
168    {
169        CURL * curl;
170        CURLcode res;
171        struct evbuffer * buf;
172        const char * scrape = inf->trackers[i].scrape;
173        char * url;
174
175        if( scrape == NULL )
176            continue;
177
178        url = tr_strdup_printf( "%s%cinfo_hash=%s",
179                                scrape,
180                                strchr( scrape, '?' ) ? '&' : '?',
181                                inf->hashEscaped );
182
183        printf( "%s ... ", url );
184        fflush( stdout );
185
186        buf = evbuffer_new( );
187        curl = tr_curl_easy_init( buf );
188        curl_easy_setopt( curl, CURLOPT_URL, url );
189        curl_easy_setopt( curl, CURLOPT_TIMEOUT, TIMEOUT_SECS );
190
191        if(( res = curl_easy_perform( curl )))
192        {
193            printf( "error: %s\n", curl_easy_strerror( res ) );
194        }
195        else
196        {
197            long response;
198            curl_easy_getinfo( curl, CURLINFO_RESPONSE_CODE, &response );
199            if( response != 200 )
200            {
201                printf( "error: unexpected response %ld \"%s\"\n",
202                        response,
203                        tr_webGetResponseStr( response ) );
204            }
205            else /* HTTP OK */
206            {
207                tr_benc top;
208                tr_benc * files;
209                tr_bool matched = FALSE;
210                const char * begin = (const char*) EVBUFFER_DATA( buf );
211                const char * end = begin + EVBUFFER_LENGTH( buf );
212
213                if( !tr_bencParse( begin, end, &top, NULL ) )
214                {
215                    if( tr_bencDictFindDict( &top, "files", &files ) )
216                    {
217                        int i = 0;
218                        tr_benc * val;
219                        const char * key;
220
221                        while( tr_bencDictChild( files, i++, &key, &val ))
222                        {
223                            if( !memcmp( inf->hash, key, SHA_DIGEST_LENGTH ) )
224                            {
225                                int64_t seeders = -1;
226                                int64_t leechers = -1;
227                                tr_bencDictFindInt( val, "complete", &seeders );
228                                tr_bencDictFindInt( val, "incomplete", &leechers );
229                                printf( "%d seeders, %d leechers\n", (int)seeders, (int)leechers );
230                                matched = TRUE;
231                            }
232                        }
233                    }
234
235                    tr_bencFree( &top );
236                }
237
238                if( !matched )
239                    printf( "no match\n" );
240            }
241        }
242
243        curl_easy_cleanup( curl );
244        evbuffer_free( buf );
245        tr_free( url );
246    }
247}
248
249int
250main( int argc, char * argv[] )
251{
252    int err;
253    tr_info inf;
254    tr_ctor * ctor;
255
256    tr_setMessageLevel( TR_MSG_ERR );
257    tr_formatter_mem_init  ( MEM_K, MEM_K_STR, MEM_M_STR, MEM_G_STR, MEM_T_STR );
258    tr_formatter_size_init ( DISK_K, DISK_K_STR, DISK_M_STR, DISK_G_STR, DISK_T_STR );
259    tr_formatter_speed_init ( SPEED_K, SPEED_K_STR, SPEED_M_STR, SPEED_G_STR, SPEED_T_STR );
260
261    if( parseCommandLine( argc, (const char**)argv ) )
262        return EXIT_FAILURE;
263
264    if( showVersion )
265    {
266        fprintf( stderr, MY_NAME" "LONG_VERSION_STRING"\n" );
267        return 0;
268    }
269
270    /* make sure the user specified a filename */
271    if( !filename )
272    {
273        fprintf( stderr, "ERROR: No .torrent file specified.\n" );
274        tr_getopt_usage( MY_NAME, getUsage( ), options );
275        fprintf( stderr, "\n" );
276        return EXIT_FAILURE;
277    }
278
279    /* try to parse the .torrent file */
280    ctor = tr_ctorNew( NULL );
281    tr_ctorSetMetainfoFromFile( ctor, filename );
282    err = tr_torrentParse( ctor, &inf );
283    tr_ctorFree( ctor );
284    if( err )
285    {
286        fprintf( stderr, "Error parsing .torrent file \"%s\"\n", filename );
287        return 1;
288    }
289
290    printf( "Name: %s\n", inf.name );
291    printf( "File: %s\n", filename );
292    printf( "\n" );
293    fflush( stdout );
294
295    if( scrapeFlag )
296        doScrape( &inf );
297    else
298        showInfo( &inf );
299
300    /* cleanup */
301    putc( '\n', stdout );
302    tr_metainfoFree( &inf );
303    return 0;
304}
Note: See TracBrowser for help on using the repository browser.