source: trunk/utils/show.c @ 12177

Last change on this file since 12177 was 12177, checked in by jordan, 11 years ago

(trunk) it's bad form to #include so many system headers in libtransmission/utils.h...

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