source: trunk/utils/show.c @ 14615

Last change on this file since 14615 was 14615, checked in by jordan, 6 years ago

fix const warnings

In particular, in passing argv around as a "const char * const *"
instead of a "const char *".

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