source: trunk/utils/show.c @ 11060

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

(trunk) #3045 "size units" -- sync the default units in all the apps s.t. they're consistent with each other

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