source: trunk/libtransmission/magnet.c

Last change on this file was 14718, checked in by mikedld, 5 years ago

Explicitly compare result of str(n)cmp/memcmp to signify that it's not boolean

  • Property svn:keywords set to Date Rev Author Id
File size: 6.8 KB
Line 
1/*
2 * This file Copyright (C) 2010-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: magnet.c 14718 2016-03-13 22:11:01Z mikedld $
8 */
9
10#include <assert.h>
11#include <string.h> /* strchr () */
12#include <stdio.h> /* sscanf () */
13
14#include "transmission.h"
15#include "crypto-utils.h" /* tr_hex_to_sha1 () */
16#include "magnet.h"
17#include "variant.h"
18#include "web.h"
19
20/***
21****
22***/
23
24/* this base32 code converted from code by Robert Kaye and Gordon Mohr
25 * and is public domain. see http://bitzi.com/publicdomain for more info */
26
27static const int base32Lookup[] =
28{
29  0xFF,0xFF,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F, /* '0', '1', '2', '3', '4', '5', '6', '7' */
30  0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, /* '8', '9', ':', ';', '<', '=', '>', '?' */
31  0xFF,0x00,0x01,0x02,0x03,0x04,0x05,0x06, /* '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G' */
32  0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E, /* 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O' */
33  0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16, /* 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W' */
34  0x17,0x18,0x19,0xFF,0xFF,0xFF,0xFF,0xFF, /* 'X', 'Y', 'Z', '[', '\', ']', '^', '_' */
35  0xFF,0x00,0x01,0x02,0x03,0x04,0x05,0x06, /* '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g' */
36  0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E, /* 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o' */
37  0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16, /* 'p', 'q', 'r', 's', 't', 'u', 'v', 'w' */
38  0x17,0x18,0x19,0xFF,0xFF,0xFF,0xFF,0xFF  /* 'x', 'y', 'z', '{', '|', '}', '~', 'DEL' */
39};
40
41static const int base32LookupLen = sizeof (base32Lookup) / sizeof (base32Lookup[0]);
42
43static void
44base32_to_sha1 (uint8_t * out, const char * in, const size_t inlen)
45{
46  const size_t outlen = 20;
47  size_t i, index, offset;
48
49  memset (out, 0, 20);
50
51  assert (inlen == 32);
52
53  for (i=0, index=0, offset=0; i<inlen; ++i)
54    {
55      int digit;
56      int lookup = in[i] - '0';
57
58      /* Skip chars outside the lookup table */
59      if (lookup<0 || lookup>=base32LookupLen)
60        continue;
61
62      /* If this digit is not in the table, ignore it */
63      digit = base32Lookup[lookup];
64      if (digit == 0xFF)
65        continue;
66
67      if (index <= 3)
68        {
69          index = (index + 5) % 8;
70          if (index == 0)
71            {
72              out[offset] |= digit;
73              offset++;
74              if (offset >= outlen)
75                break;
76            }
77          else
78            {
79              out[offset] |= digit << (8 - index);
80            }
81        }
82      else
83        {
84          index = (index + 5) % 8;
85          out[offset] |= (digit >> index);
86          offset++;
87
88          if (offset >= outlen)
89            break;
90          out[offset] |= digit << (8 - index);
91        }
92    }
93}
94
95/***
96****
97***/
98
99#define MAX_TRACKERS 64
100#define MAX_WEBSEEDS 64
101
102tr_magnet_info *
103tr_magnetParse (const char * uri)
104{
105  bool got_checksum = false;
106  int trCount = 0;
107  int wsCount = 0;
108  char * tr[MAX_TRACKERS];
109  char * ws[MAX_WEBSEEDS];
110  char * displayName = NULL;
111  uint8_t sha1[SHA_DIGEST_LENGTH];
112  tr_magnet_info * info = NULL;
113
114  if (uri != NULL && memcmp (uri, "magnet:?", 8) == 0)
115    {
116      const char * walk;
117
118      for (walk=uri+8; walk && *walk;)
119        {
120          const char * key = walk;
121          const char * delim = strchr (key, '=');
122          const char * val = delim == NULL ? NULL : delim + 1;
123          const char * next = strchr (delim == NULL ? key : val, '&');
124          size_t keylen, vallen;
125
126          if (delim != NULL)
127            keylen = (size_t) (delim - key);
128          else if (next != NULL)
129            keylen = (size_t) (next - key);
130          else
131            keylen = strlen (key);
132
133          if (val == NULL)
134            vallen = 0;
135          else if (next != NULL)
136            vallen = (size_t) (next - val);
137          else
138            vallen = strlen (val);
139
140          if (keylen == 2 && memcmp (key, "xt", 2) == 0 && val != NULL && memcmp (val, "urn:btih:", 9) == 0)
141            {
142              const char * hash = val + 9;
143              const size_t hashlen = vallen - 9;
144
145              if (hashlen == 40)
146                {
147                  tr_hex_to_sha1 (sha1, hash);
148                  got_checksum = true;
149                }
150              else if (hashlen == 32)
151                {
152                  base32_to_sha1 (sha1, hash, hashlen);
153                  got_checksum = true;
154                }
155            }
156
157          if (vallen > 0 && keylen == 2 && memcmp (key, "dn", 2) == 0)
158            displayName = tr_http_unescape (val, vallen);
159
160          if ((vallen > 0) && (trCount < MAX_TRACKERS))
161            {
162              int i;
163              if (keylen == 2 && memcmp (key, "tr", 2) == 0)
164                tr[trCount++] = tr_http_unescape (val, vallen);
165              else if ((sscanf (key, "tr.%d=", &i) == 1) && (i >= 0)) /* ticket #3341 and #5134 */
166                tr[trCount++] = tr_http_unescape (val, vallen);
167            }
168
169          if (vallen > 0 && keylen == 2 && memcmp (key, "ws", 2) == 0 && wsCount < MAX_WEBSEEDS)
170            ws[wsCount++] = tr_http_unescape (val, vallen);
171
172          walk = next != NULL ? next + 1 : NULL;
173        }
174    }
175
176  if (got_checksum)
177    {
178      info = tr_new0 (tr_magnet_info, 1);
179      info->displayName = displayName;
180      info->trackerCount = trCount;
181      info->trackers = tr_memdup (tr, sizeof (char*) * trCount);
182      info->webseedCount = wsCount;
183      info->webseeds = tr_memdup (ws, sizeof (char*) * wsCount);
184      memcpy (info->hash, sha1, sizeof (uint8_t) * SHA_DIGEST_LENGTH);
185    }
186
187  return info;
188}
189
190void
191tr_magnetFree (tr_magnet_info * info)
192{
193  if (info != NULL)
194    {
195      int i;
196
197      for (i=0; i<info->trackerCount; ++i)
198        tr_free (info->trackers[i]);
199      tr_free (info->trackers);
200
201      for (i=0; i<info->webseedCount; ++i)
202        tr_free (info->webseeds[i]);
203      tr_free (info->webseeds);
204
205      tr_free (info->displayName);
206      tr_free (info);
207    }
208}
209
210void
211tr_magnetCreateMetainfo (const tr_magnet_info * info, tr_variant * top)
212{
213  int i;
214  tr_variant * d;
215  tr_variantInitDict (top, 4);
216
217  /* announce list */
218  if (info->trackerCount == 1)
219    {
220      tr_variantDictAddStr (top, TR_KEY_announce, info->trackers[0]);
221    }
222  else
223    {
224      tr_variant * trackers = tr_variantDictAddList (top, TR_KEY_announce_list, info->trackerCount);
225      for (i=0; i<info->trackerCount; ++i)
226        tr_variantListAddStr (tr_variantListAddList (trackers, 1), info->trackers[i]);
227    }
228
229  /* webseeds */
230  if (info->webseedCount > 0)
231    {
232      tr_variant * urls = tr_variantDictAddList (top, TR_KEY_url_list, info->webseedCount);
233      for (i=0; i<info->webseedCount; ++i)
234        tr_variantListAddStr (urls, info->webseeds[i]);
235    }
236
237  /* nonstandard keys */
238  d = tr_variantDictAddDict (top, TR_KEY_magnet_info, 2);
239  tr_variantDictAddRaw (d, TR_KEY_info_hash, info->hash, 20);
240  if (info->displayName != NULL)
241    tr_variantDictAddStr (d, TR_KEY_display_name, info->displayName);
242}
243
244
Note: See TracBrowser for help on using the repository browser.