source: trunk/utils/show.c @ 10937

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

(trunk) #3045 "speed units" -- change the public API of libtransmission based on feedback from livings

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