Ticket #5770: torrent-magnet.c

File torrent-magnet.c, 12.2 KB (added by basecase, 7 years ago)

Modified torrent-magnet.c

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: torrent-magnet.c 14355 2014-12-04 12:13:59Z mikedld $
8 */
9
10#include <assert.h>
11#include <string.h> /* memcpy (), memset (), memcmp () */
12#include <stdlib.h>
13
14#include <event2/buffer.h>
15
16#include "transmission.h"
17#include "crypto-utils.h" /* tr_sha1 () */
18#include "file.h"
19#include "log.h"
20#include "magnet.h"
21#include "metainfo.h"
22#include "resume.h"
23#include "torrent.h"
24#include "torrent-magnet.h"
25#include "utils.h"
26#include "variant.h"
27#include "web.h"
28
29#define dbgmsg(tor, ...) \
30  do \
31    { \
32      if (tr_logGetDeepEnabled ()) \
33        tr_logAddDeep (__FILE__, __LINE__, tr_torrentName (tor), __VA_ARGS__); \
34    } \
35  while (0)
36
37/***
38****
39***/
40
41enum
42{
43  /* don't ask for the same metadata piece more than this often */
44  MIN_REPEAT_INTERVAL_SECS = 3
45};
46
47struct metadata_node
48{
49  time_t requestedAt;
50  int piece;
51};
52
53struct tr_incomplete_metadata
54{
55  uint8_t * metadata;
56  int metadata_size;
57  int pieceCount;
58
59  /** sorted from least to most recently requested */
60  struct metadata_node * piecesNeeded;
61  int piecesNeededCount;
62};
63
64static void
65incompleteMetadataFree (struct tr_incomplete_metadata * m)
66{
67  tr_free (m->metadata);
68  tr_free (m->piecesNeeded);
69  tr_free (m);
70}
71
72void
73tr_torrentSetMetadataSizeHint (tr_torrent * tor, int size)
74{
75  if (!tr_torrentHasMetadata (tor))
76    {
77      if (tor->incompleteMetadata == NULL)
78        {
79          int i;
80          struct tr_incomplete_metadata * m;
81          const int n = (size + (METADATA_PIECE_SIZE - 1)) / METADATA_PIECE_SIZE;
82          dbgmsg (tor, "metadata is %d bytes in %d pieces", size, n);
83
84          m = tr_new (struct tr_incomplete_metadata, 1);
85          m->pieceCount = n;
86          m->metadata = tr_new (uint8_t, size);
87          m->metadata_size = size;
88          m->piecesNeededCount = n;
89          m->piecesNeeded = tr_new (struct metadata_node, n);
90
91          for (i=0; i<n; ++i)
92            {
93              m->piecesNeeded[i].piece = i;
94              m->piecesNeeded[i].requestedAt = 0;
95            }
96
97          tor->incompleteMetadata = m;
98        }
99    }
100}
101
102static int
103findInfoDictOffset (const tr_torrent * tor)
104{
105  size_t fileLen;
106  uint8_t * fileContents;
107  int offset = 0;
108
109  /* load the file, and find the info dict's offset inside the file */
110  if ((fileContents = tr_loadFile (tor->info.torrent, &fileLen)))
111    {
112      tr_variant top;
113
114      if (!tr_variantFromBenc (&top, fileContents, fileLen))
115        {
116          tr_variant * infoDict;
117
118          if (tr_variantDictFindDict (&top, TR_KEY_info, &infoDict))
119            {
120              int infoLen;
121              char * infoContents = tr_variantToStr (infoDict, TR_VARIANT_FMT_BENC, &infoLen);
122              const uint8_t * i = (const uint8_t*) tr_memmem ((char*)fileContents, fileLen, infoContents, infoLen);
123              offset = i != NULL ? i - fileContents : 0;
124              tr_free (infoContents);
125            }
126
127          tr_variantFree (&top);
128        }
129
130      tr_free (fileContents);
131    }
132
133  return offset;
134}
135
136static void
137ensureInfoDictOffsetIsCached (tr_torrent * tor)
138{
139  assert (tr_torrentHasMetadata (tor));
140
141  if (!tor->infoDictOffsetIsCached)
142    {
143      tor->infoDictOffset = findInfoDictOffset (tor);
144      tor->infoDictOffsetIsCached = true;
145    }
146}
147
148void*
149tr_torrentGetMetadataPiece (tr_torrent * tor, int piece, int * len)
150{
151  char * ret = NULL;
152
153  assert (tr_isTorrent (tor));
154  assert (piece >= 0);
155  assert (len != NULL);
156
157  if (tr_torrentHasMetadata (tor))
158    {
159      tr_sys_file_t fd;
160
161      ensureInfoDictOffsetIsCached (tor);
162
163      assert (tor->infoDictLength > 0);
164      assert (tor->infoDictOffset >= 0);
165
166      fd = tr_sys_file_open (tor->info.torrent, TR_SYS_FILE_READ, 0, NULL);
167      if (fd != TR_BAD_SYS_FILE)
168        {
169          const int o = piece  * METADATA_PIECE_SIZE;
170
171          if (tr_sys_file_seek (fd, tor->infoDictOffset + o, TR_SEEK_SET, NULL, NULL))
172            {
173              const int l = o + METADATA_PIECE_SIZE <= tor->infoDictLength
174                          ? METADATA_PIECE_SIZE
175                          : tor->infoDictLength - o;
176
177              if (0<l && l<=METADATA_PIECE_SIZE)
178                {
179                  char * buf = tr_new (char, l);
180                  uint64_t n;
181                  if (tr_sys_file_read (fd, buf, l, &n, NULL) && n == (unsigned int) l)
182                    {
183                      *len = l;
184                      ret = buf;
185                      buf = NULL;
186                    }
187
188                  tr_free (buf);
189                }
190            }
191
192          tr_sys_file_close (fd, NULL);
193        }
194    }
195
196    return ret;
197}
198
199void
200tr_torrentSetMetadataPiece (tr_torrent  * tor, int piece, const void  * data, int len)
201{
202  int i;
203  struct tr_incomplete_metadata * m;
204  const int offset = piece * METADATA_PIECE_SIZE;
205
206  assert (tr_isTorrent (tor));
207
208  dbgmsg (tor, "got metadata piece %d", piece);
209
210  /* are we set up to download metadata? */
211  m = tor->incompleteMetadata;
212  if (m == NULL)
213    return;
214
215  /* does this data pass the smell test? */
216  if (offset + len > m->metadata_size)
217    return;
218
219  /* do we need this piece? */
220  for (i=0; i<m->piecesNeededCount; ++i)
221    if (m->piecesNeeded[i].piece == piece)
222      break;
223  if (i==m->piecesNeededCount)
224    return;
225
226  memcpy (m->metadata + offset, data, len);
227
228  tr_removeElementFromArray (m->piecesNeeded, i,
229                             sizeof (struct metadata_node),
230                             m->piecesNeededCount--);
231
232  dbgmsg (tor, "saving metainfo piece %d... %d remain", piece, m->piecesNeededCount);
233
234  /* are we done? */
235  if (m->piecesNeededCount == 0)
236    {
237      bool success = false;
238      bool checksumPassed = false;
239      bool metainfoParsed = false;
240      uint8_t sha1[SHA_DIGEST_LENGTH];
241
242      /* we've got a complete set of metainfo... see if it passes the checksum test */
243      dbgmsg (tor, "metainfo piece %d was the last one", piece);
244      tr_sha1 (sha1, m->metadata, m->metadata_size, NULL);
245      if ((checksumPassed = !memcmp (sha1, tor->info.hash, SHA_DIGEST_LENGTH)))
246        {
247          /* checksum passed; now try to parse it as benc */
248          tr_variant infoDict;
249          const int err = tr_variantFromBenc (&infoDict, m->metadata, m->metadata_size);
250          dbgmsg (tor, "err is %d", err);
251          if ((metainfoParsed = !err))
252            {
253              /* yay we have bencoded metainfo... merge it into our .torrent file */
254              tr_variant newMetainfo;
255              char * path = tr_strdup (tor->info.torrent);
256
257              if (!tr_variantFromFile (&newMetainfo, TR_VARIANT_FMT_BENC, path))
258                {
259                  bool hasInfo;
260                  tr_info info;
261                  int infoDictLength;
262
263                  /* remove any old .torrent and .resume files */
264                  tr_sys_path_remove (path, NULL);
265                  tr_torrentRemoveResume (tor);
266
267                  dbgmsg (tor, "Saving completed metadata to \"%s\"", path);
268                  tr_variantMergeDicts (tr_variantDictAddDict (&newMetainfo, TR_KEY_info, 0), &infoDict);
269
270                  memset (&info, 0, sizeof (tr_info));
271                  success = tr_metainfoParse (tor->session, &newMetainfo, &info, &hasInfo, &infoDictLength);
272
273                  if (success && !tr_getBlockSize (info.pieceSize))
274                    {
275                      tr_torrentSetLocalError (tor, "%s", _("Magnet torrent's metadata is not usable"));
276                      success = false;
277                    }
278
279                  if (success)
280                    {
281                      /* keep the new info */
282                      tor->info = info;
283                      tor->infoDictLength = infoDictLength;
284
285                      /* save the new .torrent file */
286                      tr_variantToFile (&newMetainfo, TR_VARIANT_FMT_BENC, tor->info.torrent);
287                      tr_sessionSetTorrentFile (tor->session, tor->info.hashString, tor->info.torrent);
288                      tr_torrentGotNewInfoDict (tor);
289                      tr_torrentSetDirty (tor);
290                    }
291
292                  tr_variantFree (&newMetainfo);
293                }
294
295              tr_variantFree (&infoDict);
296              tr_free (path);
297            }
298        }
299
300      if (success)
301        {
302          incompleteMetadataFree (tor->incompleteMetadata);
303          tor->incompleteMetadata = NULL;
304          tor->isStopping = true;
305          tor->magnetVerify = true;
306          tor->startAfterVerify = true;
307        }
308        else /* drat. */
309        {
310          const int n = m->pieceCount;
311          for (i=0; i<n; ++i)
312            {
313              m->piecesNeeded[i].piece = i;
314              m->piecesNeeded[i].requestedAt = 0;
315            }
316          m->piecesNeededCount = n;
317          dbgmsg (tor, "metadata error; trying again. %d pieces left", n);
318
319          tr_logAddError ("magnet status: checksum passed %d, metainfo parsed %d",
320                  (int)checksumPassed, (int)metainfoParsed);
321        }
322    }
323}
324
325bool
326tr_torrentGetNextMetadataRequest (tr_torrent * tor, time_t now, int * setme_piece)
327{
328  bool have_request = false;
329  struct tr_incomplete_metadata * m;
330
331  assert (tr_isTorrent (tor));
332
333  m = tor->incompleteMetadata;
334
335  if ((m != NULL) && (m->piecesNeededCount > 0)
336                  && (m->piecesNeeded[0].requestedAt + MIN_REPEAT_INTERVAL_SECS < now))
337    {
338      int i;
339      const int piece = m->piecesNeeded[0].piece;
340
341      tr_removeElementFromArray (m->piecesNeeded, 0,
342                                 sizeof (struct metadata_node),
343                                 m->piecesNeededCount--);
344
345      i = m->piecesNeededCount++;
346      m->piecesNeeded[i].piece = piece;
347      m->piecesNeeded[i].requestedAt = now;
348
349      dbgmsg (tor, "next piece to request: %d", piece);
350      *setme_piece = piece;
351      have_request = true;
352    }
353
354  return have_request;
355}
356
357double
358tr_torrentGetMetadataPercent (const tr_torrent * tor)
359{
360  double ret;
361
362  if (tr_torrentHasMetadata (tor))
363    {
364      ret = 1.0;
365    }
366  else
367    {
368      const struct tr_incomplete_metadata * m = tor->incompleteMetadata;
369
370      if (!m || !m->pieceCount)
371        ret = 0.0;
372      else
373        ret = (m->pieceCount - m->piecesNeededCount) / (double)m->pieceCount;
374    }
375
376  return ret;
377}
378
379/* FIXME: this should be renamed tr_metainfoGetMagnetLink() and moved to metainfo.c for consistency */
380char *
381tr_torrentInfoGetMagnetLink (const tr_info * inf, const char * downloadLocation)
382{
383 
384  unsigned int i;
385  const char * name;
386  struct evbuffer * s = evbuffer_new ();
387  evbuffer_add_printf (s, "magnet:?xt=urn:btih:%s", inf->hashString);
388
389  name = inf->name;
390  if (name && *name)
391    {
392      evbuffer_add_printf (s, "%s", "&dn=");
393      tr_http_escape (s, name, -1, true);
394    }
395
396  for (i=0; i<inf->trackerCount; ++i)
397    {
398      evbuffer_add_printf (s, "%s", "&tr=");
399      tr_http_escape (s, inf->trackers[i].announce, -1, true);
400    }
401
402  for (i=0; i<inf->webseedCount; i++)
403    {
404      evbuffer_add_printf (s, "%s", "&ws=");
405      tr_http_escape (s, inf->webseeds[i], -1, true);
406    }
407   
408/* Copy completed magnetLINK to BUFF and Send to be copied to .magnetLink File in download Directory*/
409        char * buff[255];                                                                        //Create buffer to hold mgnetlink 
410        size_t len = evbuffer_get_length(s);
411        evbuffer_copyout(s, buff, len );
412       
413        tr_writeMagnetToFile (inf, downloadLocation, buff);
414        return evbuffer_free_to_str (s);
415}
416
417void tr_writeMagnetToFile (const tr_info * infomag, const char * downloadLocation, const char * magURL)
418{       
419
420//To do set file location to torrent directory and set file name to torrent name
421//To do: set path
422//Working: set magFileName to name of torrent and add extension .magnetLINK
423//                      open file and write magnetlilnk to file
424       
425        printf( " Entering th writ magnet to filefunction \n");
426        char  downloadDIR[255];
427        memcpy(downloadDIR, downloadLocation,  strlen(downloadLocation)+1);
428printf(" This is the download Diectory- %s\n", downloadLocation);
429        char * magFileName = downloadDIR;
430        strcat (magFileName, "/");
431        strcat (magFileName, infomag->name);
432        strcat (magFileName, ".magnetLINK");
433        FILE *fil = fopen (magFileName, "w");                    //create filelink for NAME.magnetlink
434  if (fil == NULL)                                                                       //Check if file could be opened       
435  {
436    printf("Error opening file!\n");
437   
438  } 
439 fprintf (fil, "%s", magURL); 
440printf ("before exit and file close\n"); 
441        fclose (fil);
442printf (" File closed sucessfully\n");
443}