source: trunk/libtransmission/resume.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: 25.4 KB
Line 
1/*
2 * This file Copyright (C) 2008-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: resume.c 14718 2016-03-13 22:11:01Z mikedld $
8 */
9
10#include <string.h>
11
12#include "transmission.h"
13#include "completion.h"
14#include "error.h"
15#include "file.h"
16#include "log.h"
17#include "metainfo.h" /* tr_metainfoGetBasename () */
18#include "peer-mgr.h" /* pex */
19#include "platform.h" /* tr_getResumeDir () */
20#include "resume.h"
21#include "session.h"
22#include "torrent.h"
23#include "utils.h" /* tr_buildPath */
24#include "variant.h"
25
26enum
27{
28  MAX_REMEMBERED_PEERS = 200
29};
30
31static char*
32getResumeFilename (const tr_torrent * tor)
33{
34  char * base = tr_metainfoGetBasename (tr_torrentInfo (tor));
35  char * filename = tr_strdup_printf ("%s" TR_PATH_DELIMITER_STR "%s.resume",
36                                      tr_getResumeDir (tor->session), base);
37  tr_free (base);
38  return filename;
39}
40
41/***
42****
43***/
44
45static void
46savePeers (tr_variant * dict, const tr_torrent * tor)
47{
48  int count;
49  tr_pex * pex;
50
51  count = tr_peerMgrGetPeers ((tr_torrent*) tor, &pex, TR_AF_INET, TR_PEERS_INTERESTING, MAX_REMEMBERED_PEERS);
52  if (count > 0)
53    tr_variantDictAddRaw (dict, TR_KEY_peers2, pex, sizeof (tr_pex) * count);
54  tr_free (pex);
55
56  count = tr_peerMgrGetPeers ((tr_torrent*) tor, &pex, TR_AF_INET6, TR_PEERS_INTERESTING, MAX_REMEMBERED_PEERS);
57  if (count > 0)
58    tr_variantDictAddRaw (dict, TR_KEY_peers2_6, pex, sizeof (tr_pex) * count);
59  tr_free (pex);
60}
61
62static int
63addPeers (tr_torrent * tor, const uint8_t * buf, int buflen)
64{
65  int i;
66  int numAdded = 0;
67  const int count = buflen / sizeof (tr_pex);
68
69  for (i=0; i<count && numAdded<MAX_REMEMBERED_PEERS; ++i)
70    {
71      tr_pex pex;
72      memcpy (&pex, buf + (i * sizeof (tr_pex)), sizeof (tr_pex));
73      if (tr_isPex (&pex))
74        {
75          tr_peerMgrAddPex (tor, TR_PEER_FROM_RESUME, &pex, -1);
76          ++numAdded;
77        }
78    }
79
80  return numAdded;
81}
82
83
84static uint64_t
85loadPeers (tr_variant * dict, tr_torrent * tor)
86{
87  const uint8_t * str;
88  size_t len;
89  uint64_t ret = 0;
90
91  if (tr_variantDictFindRaw (dict, TR_KEY_peers2, &str, &len))
92    {
93      const int numAdded = addPeers (tor, str, len);
94      tr_logAddTorDbg (tor, "Loaded %d IPv4 peers from resume file", numAdded);
95      ret = TR_FR_PEERS;
96    }
97
98  if (tr_variantDictFindRaw (dict, TR_KEY_peers2_6, &str, &len))
99    {
100      const int numAdded = addPeers (tor, str, len);
101      tr_logAddTorDbg (tor, "Loaded %d IPv6 peers from resume file", numAdded);
102      ret = TR_FR_PEERS;
103    }
104
105  return ret;
106}
107
108/***
109****
110***/
111
112static void
113saveDND (tr_variant * dict, const tr_torrent * tor)
114{
115  tr_variant * list;
116  tr_file_index_t i;
117  const tr_info * const inf = tr_torrentInfo (tor);
118  const tr_file_index_t n = inf->fileCount;
119
120  list = tr_variantDictAddList (dict, TR_KEY_dnd, n);
121  for (i=0; i<n; ++i)
122    tr_variantListAddInt (list, inf->files[i].dnd ? 1 : 0);
123}
124
125static uint64_t
126loadDND (tr_variant * dict, tr_torrent * tor)
127{
128  uint64_t ret = 0;
129  tr_variant * list = NULL;
130  const tr_file_index_t n = tor->info.fileCount;
131
132  if (tr_variantDictFindList (dict, TR_KEY_dnd, &list)
133      && (tr_variantListSize (list) == n))
134    {
135      int64_t tmp;
136      tr_file_index_t * dl = tr_new (tr_file_index_t, n);
137      tr_file_index_t * dnd = tr_new (tr_file_index_t, n);
138      tr_file_index_t i, dlCount = 0, dndCount = 0;
139
140      for (i=0; i<n; ++i)
141        {
142          if (tr_variantGetInt (tr_variantListChild (list, i), &tmp) && tmp)
143            dnd[dndCount++] = i;
144          else
145            dl[dlCount++] = i;
146        }
147
148      if (dndCount)
149        {
150          tr_torrentInitFileDLs (tor, dnd, dndCount, false);
151          tr_logAddTorDbg (tor, "Resume file found %d files listed as dnd", dndCount);
152        }
153
154      if (dlCount)
155        {
156          tr_torrentInitFileDLs (tor, dl, dlCount, true);
157          tr_logAddTorDbg (tor, "Resume file found %d files marked for download", dlCount);
158        }
159
160      tr_free (dnd);
161      tr_free (dl);
162      ret = TR_FR_DND;
163    }
164  else
165    {
166      tr_logAddTorDbg (tor, "Couldn't load DND flags. DND list (%p) has %zu"" children; torrent has %d files",
167                       (void*)list, tr_variantListSize (list), (int)n);
168    }
169
170  return ret;
171}
172
173/***
174****
175***/
176
177static void
178saveFilePriorities (tr_variant * dict, const tr_torrent * tor)
179{
180  tr_variant * list;
181  tr_file_index_t i;
182  const tr_info * const inf = tr_torrentInfo (tor);
183  const tr_file_index_t n = inf->fileCount;
184
185  list = tr_variantDictAddList (dict, TR_KEY_priority, n);
186  for (i=0; i<n; ++i)
187    tr_variantListAddInt (list, inf->files[i].priority);
188}
189
190static uint64_t
191loadFilePriorities (tr_variant * dict, tr_torrent * tor)
192{
193  tr_variant * list;
194  uint64_t ret = 0;
195  const tr_file_index_t n = tor->info.fileCount;
196
197  if (tr_variantDictFindList (dict, TR_KEY_priority, &list)
198      && (tr_variantListSize (list) == n))
199    {
200      int64_t priority;
201      tr_file_index_t i;
202
203      for (i=0; i<n; ++i)
204        if (tr_variantGetInt (tr_variantListChild (list, i), &priority))
205          tr_torrentInitFilePriority (tor, i, priority);
206
207      ret = TR_FR_FILE_PRIORITIES;
208    }
209
210    return ret;
211}
212
213/***
214****
215***/
216
217static void
218saveSingleSpeedLimit (tr_variant * d, tr_torrent * tor, tr_direction dir)
219{
220  tr_variantDictReserve (d, 3);
221  tr_variantDictAddInt (d, TR_KEY_speed_Bps, tr_torrentGetSpeedLimit_Bps (tor, dir));
222  tr_variantDictAddBool (d, TR_KEY_use_global_speed_limit, tr_torrentUsesSessionLimits (tor));
223  tr_variantDictAddBool (d, TR_KEY_use_speed_limit, tr_torrentUsesSpeedLimit (tor, dir));
224}
225
226static void
227saveSpeedLimits (tr_variant * dict, tr_torrent * tor)
228{
229  saveSingleSpeedLimit (tr_variantDictAddDict (dict, TR_KEY_speed_limit_down, 0), tor, TR_DOWN);
230  saveSingleSpeedLimit (tr_variantDictAddDict (dict, TR_KEY_speed_limit_up, 0), tor, TR_UP);
231}
232
233static void
234saveRatioLimits (tr_variant * dict, tr_torrent * tor)
235{
236  tr_variant * d = tr_variantDictAddDict (dict, TR_KEY_ratio_limit, 2);
237  tr_variantDictAddReal (d, TR_KEY_ratio_limit, tr_torrentGetRatioLimit (tor));
238  tr_variantDictAddInt (d, TR_KEY_ratio_mode, tr_torrentGetRatioMode (tor));
239}
240
241static void
242saveIdleLimits (tr_variant * dict, tr_torrent * tor)
243{
244  tr_variant * d = tr_variantDictAddDict (dict, TR_KEY_idle_limit, 2);
245  tr_variantDictAddInt (d, TR_KEY_idle_limit, tr_torrentGetIdleLimit (tor));
246  tr_variantDictAddInt (d, TR_KEY_idle_mode, tr_torrentGetIdleMode (tor));
247}
248
249static void
250loadSingleSpeedLimit (tr_variant * d, tr_direction dir, tr_torrent * tor)
251{
252  int64_t i;
253  bool boolVal;
254
255  if (tr_variantDictFindInt (d, TR_KEY_speed_Bps, &i))
256    tr_torrentSetSpeedLimit_Bps (tor, dir, i);
257  else if (tr_variantDictFindInt (d, TR_KEY_speed, &i))
258    tr_torrentSetSpeedLimit_Bps (tor, dir, i*1024);
259
260  if (tr_variantDictFindBool (d, TR_KEY_use_speed_limit, &boolVal))
261    tr_torrentUseSpeedLimit (tor, dir, boolVal);
262
263  if (tr_variantDictFindBool (d, TR_KEY_use_global_speed_limit, &boolVal))
264    tr_torrentUseSessionLimits (tor, boolVal);
265}
266
267static uint64_t
268loadSpeedLimits (tr_variant * dict, tr_torrent * tor)
269{
270  tr_variant * d;
271  uint64_t ret = 0;
272
273  if (tr_variantDictFindDict (dict, TR_KEY_speed_limit_up, &d))
274    {
275      loadSingleSpeedLimit (d, TR_UP, tor);
276      ret = TR_FR_SPEEDLIMIT;
277    }
278
279  if (tr_variantDictFindDict (dict, TR_KEY_speed_limit_down, &d))
280    {
281      loadSingleSpeedLimit (d, TR_DOWN, tor);
282      ret = TR_FR_SPEEDLIMIT;
283    }
284
285  return ret;
286}
287
288static uint64_t
289loadRatioLimits (tr_variant * dict, tr_torrent * tor)
290{
291  tr_variant * d;
292  uint64_t ret = 0;
293
294  if (tr_variantDictFindDict (dict, TR_KEY_ratio_limit, &d))
295    {
296      int64_t i;
297      double dratio;
298
299      if (tr_variantDictFindReal (d, TR_KEY_ratio_limit, &dratio))
300        tr_torrentSetRatioLimit (tor, dratio);
301
302      if (tr_variantDictFindInt (d, TR_KEY_ratio_mode, &i))
303        tr_torrentSetRatioMode (tor, i);
304
305      ret = TR_FR_RATIOLIMIT;
306    }
307
308  return ret;
309}
310
311static uint64_t
312loadIdleLimits (tr_variant * dict, tr_torrent * tor)
313{
314  tr_variant * d;
315  uint64_t ret = 0;
316
317  if (tr_variantDictFindDict (dict, TR_KEY_idle_limit, &d))
318    {
319      int64_t i;
320      int64_t imin;
321
322      if (tr_variantDictFindInt (d, TR_KEY_idle_limit, &imin))
323        tr_torrentSetIdleLimit (tor, imin);
324
325      if (tr_variantDictFindInt (d, TR_KEY_idle_mode, &i))
326        tr_torrentSetIdleMode (tor, i);
327
328      ret = TR_FR_IDLELIMIT;
329    }
330
331  return ret;
332}
333
334/***
335****
336***/
337
338static void
339saveName (tr_variant * dict, const tr_torrent * tor)
340{
341  tr_variantDictAddStr (dict, TR_KEY_name, tr_torrentName(tor));
342}
343
344static uint64_t
345loadName (tr_variant * dict, tr_torrent * tor)
346{
347  uint64_t ret = 0;
348  const char * name;
349
350  if (tr_variantDictFindStr (dict, TR_KEY_name, &name, NULL))
351    {
352      ret = TR_FR_NAME;
353
354      if (tr_strcmp0 (tr_torrentName(tor), name) != 0)
355        {
356          tr_free (tor->info.name);
357          tor->info.name = tr_strdup (name);
358        }
359    }
360
361  return ret;
362}
363
364/***
365****
366***/
367
368static void
369saveFilenames (tr_variant * dict, const tr_torrent * tor)
370{
371  tr_file_index_t i;
372  bool any_renamed;
373  const tr_file_index_t n = tor->info.fileCount;
374  const tr_file * files = tor->info.files;
375
376  any_renamed = false;
377  for (i=0; !any_renamed && i<n; ++i)
378    any_renamed = files[i].is_renamed;
379
380  if (any_renamed)
381    {
382      tr_variant * list = tr_variantDictAddList (dict, TR_KEY_files, n);
383
384      for (i=0; i<n; ++i)
385        tr_variantListAddStr (list, files[i].is_renamed ? files[i].name : "");
386    }
387}
388
389static uint64_t
390loadFilenames (tr_variant * dict, tr_torrent * tor)
391{
392  tr_variant * list;
393  uint64_t ret = 0;
394
395  if (tr_variantDictFindList (dict, TR_KEY_files, &list))
396    {
397      size_t i;
398      const size_t n = tr_variantListSize (list);
399      tr_file * files = tor->info.files;
400
401      for (i=0; i<tor->info.fileCount && i<n; ++i)
402        {
403          const char * str;
404          size_t str_len;
405          if (tr_variantGetStr (tr_variantListChild (list, i), &str, &str_len) && str && str_len)
406            {
407              tr_free (files[i].name);
408              files[i].name = tr_strndup (str, str_len);
409              files[i].is_renamed = true;
410            }
411        }
412
413      ret = TR_FR_FILENAMES;
414    }
415
416  return ret;
417}
418
419/***
420****
421***/
422
423static void
424bitfieldToBenc (const tr_bitfield * b, tr_variant * benc)
425{
426  if (tr_bitfieldHasAll (b))
427    {
428      tr_variantInitStr (benc, "all", 3);
429    }
430  else if (tr_bitfieldHasNone (b))
431    {
432      tr_variantInitStr (benc, "none", 4);
433    }
434  else
435    {
436      size_t byte_count = 0;
437      uint8_t * raw = tr_bitfieldGetRaw (b, &byte_count);
438      tr_variantInitRaw (benc, raw, byte_count);
439      tr_free (raw);
440    }
441}
442
443
444static void
445saveProgress (tr_variant * dict, tr_torrent * tor)
446{
447  tr_variant * l;
448  tr_variant * prog;
449  tr_file_index_t fi;
450  const tr_info * inf = tr_torrentInfo (tor);
451  const time_t now = tr_time ();
452
453  prog = tr_variantDictAddDict (dict, TR_KEY_progress, 3);
454
455  /* add the file/piece check timestamps... */
456  l = tr_variantDictAddList (prog, TR_KEY_time_checked, inf->fileCount);
457  for (fi=0; fi<inf->fileCount; ++fi)
458    {
459      const tr_piece * p;
460      const tr_piece * pend;
461      time_t oldest_nonzero = now;
462      time_t newest = 0;
463      bool has_zero = false;
464      const time_t mtime = tr_torrentGetFileMTime (tor, fi);
465      const tr_file * f = &inf->files[fi];
466
467      /* get the oldest and newest nonzero timestamps for pieces in this file */
468      for (p=&inf->pieces[f->firstPiece], pend=&inf->pieces[f->lastPiece]; p!=pend; ++p)
469        {
470          if (!p->timeChecked)
471            has_zero = true;
472          else if (oldest_nonzero > p->timeChecked)
473            oldest_nonzero = p->timeChecked;
474
475          if (newest < p->timeChecked)
476            newest = p->timeChecked;
477        }
478
479      /* If some of a file's pieces have been checked more recently than
480         the file's mtime, and some less recently, then that file will
481         have a list containing timestamps for each piece.
482
483         However, the most common use case is that the file doesn't change
484         after it's downloaded. To reduce overhead in the .resume file,
485         only a single timestamp is saved for the file if *all* or *none*
486         of the pieces were tested more recently than the file's mtime. */
487
488      if (!has_zero && (mtime <= oldest_nonzero)) /* all checked */
489        {
490          tr_variantListAddInt (l, oldest_nonzero);
491        }
492      else if (newest < mtime) /* none checked */
493        {
494          tr_variantListAddInt (l, newest);
495        }
496      else /* some are checked, some aren't... so list piece by piece */
497        {
498          const int offset = oldest_nonzero - 1;
499          tr_variant * ll = tr_variantListAddList (l, 2 + f->lastPiece - f->firstPiece);
500          tr_variantListAddInt (ll, offset);
501          for (p=&inf->pieces[f->firstPiece], pend=&inf->pieces[f->lastPiece]+1; p!=pend; ++p)
502            tr_variantListAddInt (ll, p->timeChecked ? p->timeChecked - offset : 0);
503        }
504    }
505
506  /* add the progress */
507  if (tor->completeness == TR_SEED)
508    tr_variantDictAddStr (prog, TR_KEY_have, "all");
509
510  /* add the blocks bitfield */
511  bitfieldToBenc (&tor->completion.blockBitfield, tr_variantDictAdd (prog, TR_KEY_blocks));
512}
513
514static uint64_t
515loadProgress (tr_variant * dict, tr_torrent * tor)
516{
517  size_t i, n;
518  uint64_t ret = 0;
519  tr_variant * prog;
520  const tr_info * inf = tr_torrentInfo (tor);
521
522  for (i=0, n=inf->pieceCount; i<n; ++i)
523    inf->pieces[i].timeChecked = 0;
524
525  if (tr_variantDictFindDict (dict, TR_KEY_progress, &prog))
526    {
527      const char * err;
528      const char * str;
529      const uint8_t * raw;
530      size_t rawlen;
531      tr_variant * l;
532      tr_variant * b;
533      struct tr_bitfield blocks = TR_BITFIELD_INIT;
534
535      if (tr_variantDictFindList (prog, TR_KEY_time_checked, &l))
536        {
537          /* per-piece timestamps were added in 2.20.
538
539             If some of a file's pieces have been checked more recently than
540             the file's mtime, and some lest recently, then that file will
541             have a list containing timestamps for each piece.
542
543             However, the most common use case is that the file doesn't change
544             after it's downloaded. To reduce overhead in the .resume file,
545             only a single timestamp is saved for the file if *all* or *none*
546             of the pieces were tested more recently than the file's mtime. */
547
548          tr_file_index_t fi;
549
550          for (fi=0; fi<inf->fileCount; ++fi)
551            {
552              tr_variant * b = tr_variantListChild (l, fi);
553              const tr_file * f = &inf->files[fi];
554              tr_piece * p = &inf->pieces[f->firstPiece];
555              const tr_piece * pend = &inf->pieces[f->lastPiece]+1;
556
557              if (tr_variantIsInt (b))
558                {
559                  int64_t t;
560                  tr_variantGetInt (b, &t);
561                  for (; p!=pend; ++p)
562                    p->timeChecked = (time_t)t;
563                }
564              else if (tr_variantIsList (b))
565                {
566                  int i = 0;
567                  int64_t offset = 0;
568                  const int pieces = f->lastPiece + 1 - f->firstPiece;
569
570                  tr_variantGetInt (tr_variantListChild (b, 0), &offset);
571
572                  for (i=0; i<pieces; ++i)
573                    {
574                      int64_t t = 0;
575                      tr_variantGetInt (tr_variantListChild (b, i+1), &t);
576                      inf->pieces[f->firstPiece+i].timeChecked = (time_t)(t ? t + offset : 0);
577                    }
578                }
579            }
580        }
581      else if (tr_variantDictFindList (prog, TR_KEY_mtimes, &l))
582        {
583          tr_file_index_t fi;
584
585          /* Before 2.20, we stored the files' mtimes in the .resume file.
586             When loading the .resume file, a torrent's file would be flagged
587             as untested if its stored mtime didn't match its real mtime. */
588
589          for (fi=0; fi<inf->fileCount; ++fi)
590            {
591              int64_t t;
592
593              if (tr_variantGetInt (tr_variantListChild (l, fi), &t))
594                {
595                  const tr_file * f = &inf->files[fi];
596                  tr_piece * p = &inf->pieces[f->firstPiece];
597                  const tr_piece * pend = &inf->pieces[f->lastPiece];
598                  const time_t mtime = tr_torrentGetFileMTime (tor, fi);
599                  const time_t timeChecked = mtime==t ? mtime : 0;
600
601                  for (; p!=pend; ++p)
602                    p->timeChecked = timeChecked;
603                }
604            }
605        }
606
607      err = NULL;
608      tr_bitfieldConstruct (&blocks, tor->blockCount);
609
610      if ((b = tr_variantDictFind (prog, TR_KEY_blocks)))
611        {
612          size_t buflen;
613          const uint8_t * buf;
614
615          if (!tr_variantGetRaw (b, &buf, &buflen))
616            err = "Invalid value for \"blocks\"";
617          else if (buflen == 3 && memcmp (buf, "all", 3) == 0)
618            tr_bitfieldSetHasAll (&blocks);
619          else if (buflen == 4 && memcmp (buf, "none", 4) == 0)
620            tr_bitfieldSetHasNone (&blocks);
621          else
622            tr_bitfieldSetRaw (&blocks, buf, buflen, true);
623        }
624      else if (tr_variantDictFindStr (prog, TR_KEY_have, &str, NULL))
625        {
626          if (strcmp (str, "all") == 0)
627            tr_bitfieldSetHasAll (&blocks);
628          else
629            err = "Invalid value for HAVE";
630        }
631      else if (tr_variantDictFindRaw (prog, TR_KEY_bitfield, &raw, &rawlen))
632        {
633          tr_bitfieldSetRaw (&blocks, raw, rawlen, true);
634        }
635      else err = "Couldn't find 'pieces' or 'have' or 'bitfield'";
636
637      if (err != NULL)
638        tr_logAddTorDbg (tor, "Torrent needs to be verified - %s", err);
639      else
640        tr_cpBlockInit (&tor->completion, &blocks);
641
642      tr_bitfieldDestruct (&blocks);
643      ret = TR_FR_PROGRESS;
644    }
645
646  return ret;
647}
648
649/***
650****
651***/
652
653void
654tr_torrentSaveResume (tr_torrent * tor)
655{
656  int err;
657  tr_variant top;
658  char * filename;
659
660  if (!tr_isTorrent (tor))
661    return;
662
663  tr_variantInitDict (&top, 50); /* arbitrary "big enough" number */
664  tr_variantDictAddInt (&top, TR_KEY_seeding_time_seconds, tor->secondsSeeding);
665  tr_variantDictAddInt (&top, TR_KEY_downloading_time_seconds, tor->secondsDownloading);
666  tr_variantDictAddInt (&top, TR_KEY_activity_date, tor->activityDate);
667  tr_variantDictAddInt (&top, TR_KEY_added_date, tor->addedDate);
668  tr_variantDictAddInt (&top, TR_KEY_corrupt, tor->corruptPrev + tor->corruptCur);
669  tr_variantDictAddInt (&top, TR_KEY_done_date, tor->doneDate);
670  tr_variantDictAddStr (&top, TR_KEY_destination, tor->downloadDir);
671  if (tor->incompleteDir != NULL)
672    tr_variantDictAddStr (&top, TR_KEY_incomplete_dir, tor->incompleteDir);
673  tr_variantDictAddInt (&top, TR_KEY_downloaded, tor->downloadedPrev + tor->downloadedCur);
674  tr_variantDictAddInt (&top, TR_KEY_uploaded, tor->uploadedPrev + tor->uploadedCur);
675  tr_variantDictAddInt (&top, TR_KEY_max_peers, tor->maxConnectedPeers);
676  tr_variantDictAddInt (&top, TR_KEY_bandwidth_priority, tr_torrentGetPriority (tor));
677  tr_variantDictAddBool (&top, TR_KEY_paused, !tor->isRunning && !tor->isQueued);
678  savePeers (&top, tor);
679  if (tr_torrentHasMetadata (tor))
680    {
681      saveFilePriorities (&top, tor);
682      saveDND (&top, tor);
683      saveProgress (&top, tor);
684    }
685  saveSpeedLimits (&top, tor);
686  saveRatioLimits (&top, tor);
687  saveIdleLimits (&top, tor);
688  saveFilenames (&top, tor);
689  saveName (&top, tor);
690
691  filename = getResumeFilename (tor);
692  if ((err = tr_variantToFile (&top, TR_VARIANT_FMT_BENC, filename)))
693    tr_torrentSetLocalError (tor, "Unable to save resume file: %s", tr_strerror (err));
694  tr_free (filename);
695
696  tr_variantFree (&top);
697}
698
699static uint64_t
700loadFromFile (tr_torrent * tor, uint64_t fieldsToLoad)
701{
702  size_t len;
703  int64_t  i;
704  const char * str;
705  char * filename;
706  tr_variant top;
707  bool boolVal;
708  uint64_t fieldsLoaded = 0;
709  const bool wasDirty = tor->isDirty;
710  tr_error * error = NULL;
711
712  assert (tr_isTorrent (tor));
713
714  filename = getResumeFilename (tor);
715
716  if (!tr_variantFromFile (&top, TR_VARIANT_FMT_BENC, filename, &error))
717    {
718      tr_logAddTorDbg (tor, "Couldn't read \"%s\": %s", filename, error->message);
719      tr_error_free (error);
720
721      tr_free (filename);
722      return fieldsLoaded;
723    }
724
725  tr_logAddTorDbg (tor, "Read resume file \"%s\"", filename);
726
727  if ((fieldsToLoad & TR_FR_CORRUPT)
728      && tr_variantDictFindInt (&top, TR_KEY_corrupt, &i))
729    {
730      tor->corruptPrev = i;
731      fieldsLoaded |= TR_FR_CORRUPT;
732    }
733
734  if ((fieldsToLoad & (TR_FR_PROGRESS | TR_FR_DOWNLOAD_DIR))
735      && (tr_variantDictFindStr (&top, TR_KEY_destination, &str, &len))
736      && (str && *str))
737    {
738      const bool is_current_dir = tor->currentDir == tor->downloadDir;
739      tr_free (tor->downloadDir);
740      tor->downloadDir = tr_strndup (str, len);
741      if (is_current_dir)
742        tor->currentDir = tor->downloadDir;
743      fieldsLoaded |= TR_FR_DOWNLOAD_DIR;
744    }
745
746  if ((fieldsToLoad & (TR_FR_PROGRESS | TR_FR_INCOMPLETE_DIR))
747      && (tr_variantDictFindStr (&top, TR_KEY_incomplete_dir, &str, &len))
748      && (str && *str))
749    {
750      const bool is_current_dir = tor->currentDir == tor->incompleteDir;
751      tr_free (tor->incompleteDir);
752      tor->incompleteDir = tr_strndup (str, len);
753      if (is_current_dir)
754        tor->currentDir = tor->incompleteDir;
755      fieldsLoaded |= TR_FR_INCOMPLETE_DIR;
756    }
757
758  if ((fieldsToLoad & TR_FR_DOWNLOADED)
759      && tr_variantDictFindInt (&top, TR_KEY_downloaded, &i))
760    {
761      tor->downloadedPrev = i;
762      fieldsLoaded |= TR_FR_DOWNLOADED;
763    }
764
765  if ((fieldsToLoad & TR_FR_UPLOADED)
766      && tr_variantDictFindInt (&top, TR_KEY_uploaded, &i))
767    {
768      tor->uploadedPrev = i;
769      fieldsLoaded |= TR_FR_UPLOADED;
770    }
771
772  if ((fieldsToLoad & TR_FR_MAX_PEERS)
773      && tr_variantDictFindInt (&top, TR_KEY_max_peers, &i))
774    {
775      tor->maxConnectedPeers = i;
776      fieldsLoaded |= TR_FR_MAX_PEERS;
777    }
778
779  if ((fieldsToLoad & TR_FR_RUN)
780      && tr_variantDictFindBool (&top, TR_KEY_paused, &boolVal))
781    {
782      tor->isRunning = !boolVal;
783      fieldsLoaded |= TR_FR_RUN;
784    }
785
786  if ((fieldsToLoad & TR_FR_ADDED_DATE)
787      && tr_variantDictFindInt (&top, TR_KEY_added_date, &i))
788    {
789      tor->addedDate = i;
790      fieldsLoaded |= TR_FR_ADDED_DATE;
791    }
792
793  if ((fieldsToLoad & TR_FR_DONE_DATE)
794      && tr_variantDictFindInt (&top, TR_KEY_done_date, &i))
795    {
796      tor->doneDate = i;
797      fieldsLoaded |= TR_FR_DONE_DATE;
798    }
799
800  if ((fieldsToLoad & TR_FR_ACTIVITY_DATE)
801      && tr_variantDictFindInt (&top, TR_KEY_activity_date, &i))
802    {
803      tr_torrentSetActivityDate (tor, i);
804      fieldsLoaded |= TR_FR_ACTIVITY_DATE;
805    }
806
807  if ((fieldsToLoad & TR_FR_TIME_SEEDING)
808      && tr_variantDictFindInt (&top, TR_KEY_seeding_time_seconds, &i))
809    {
810      tor->secondsSeeding = i;
811      fieldsLoaded |= TR_FR_TIME_SEEDING;
812    }
813
814  if ((fieldsToLoad & TR_FR_TIME_DOWNLOADING)
815      && tr_variantDictFindInt (&top, TR_KEY_downloading_time_seconds, &i))
816    {
817      tor->secondsDownloading = i;
818      fieldsLoaded |= TR_FR_TIME_DOWNLOADING;
819    }
820
821  if ((fieldsToLoad & TR_FR_BANDWIDTH_PRIORITY)
822      && tr_variantDictFindInt (&top, TR_KEY_bandwidth_priority, &i)
823      && tr_isPriority (i))
824    {
825      tr_torrentSetPriority (tor, i);
826      fieldsLoaded |= TR_FR_BANDWIDTH_PRIORITY;
827    }
828
829  if (fieldsToLoad & TR_FR_PEERS)
830    fieldsLoaded |= loadPeers (&top, tor);
831
832  if (fieldsToLoad & TR_FR_FILE_PRIORITIES)
833    fieldsLoaded |= loadFilePriorities (&top, tor);
834
835  if (fieldsToLoad & TR_FR_PROGRESS)
836    fieldsLoaded |= loadProgress (&top, tor);
837
838  if (fieldsToLoad & TR_FR_DND)
839    fieldsLoaded |= loadDND (&top, tor);
840
841  if (fieldsToLoad & TR_FR_SPEEDLIMIT)
842    fieldsLoaded |= loadSpeedLimits (&top, tor);
843
844  if (fieldsToLoad & TR_FR_RATIOLIMIT)
845    fieldsLoaded |= loadRatioLimits (&top, tor);
846
847  if (fieldsToLoad & TR_FR_IDLELIMIT)
848    fieldsLoaded |= loadIdleLimits (&top, tor);
849
850  if (fieldsToLoad & TR_FR_FILENAMES)
851    fieldsLoaded |= loadFilenames (&top, tor);
852
853  if (fieldsToLoad & TR_FR_NAME)
854    fieldsLoaded |= loadName (&top, tor);
855
856  /* loading the resume file triggers of a lot of changes,
857   * but none of them needs to trigger a re-saving of the
858   * same resume information... */
859  tor->isDirty = wasDirty;
860
861  tr_variantFree (&top);
862  tr_free (filename);
863  return fieldsLoaded;
864}
865
866static uint64_t
867setFromCtor (tr_torrent * tor, uint64_t fields, const tr_ctor * ctor, int mode)
868{
869  uint64_t ret = 0;
870
871  if (fields & TR_FR_RUN)
872    {
873      bool isPaused;
874
875      if (tr_ctorGetPaused (ctor, mode, &isPaused))
876        {
877          tor->isRunning = !isPaused;
878          ret |= TR_FR_RUN;
879        }
880    }
881
882  if (fields & TR_FR_MAX_PEERS)
883    if (tr_ctorGetPeerLimit (ctor, mode, &tor->maxConnectedPeers))
884      ret |= TR_FR_MAX_PEERS;
885
886  if (fields & TR_FR_DOWNLOAD_DIR)
887    {
888      const char * path;
889
890      if (tr_ctorGetDownloadDir (ctor, mode, &path) && path && *path)
891        {
892          ret |= TR_FR_DOWNLOAD_DIR;
893          tr_free (tor->downloadDir);
894          tor->downloadDir = tr_strdup (path);
895        }
896    }
897
898  return ret;
899}
900
901static uint64_t
902useManditoryFields (tr_torrent * tor, uint64_t fields, const tr_ctor * ctor)
903{
904  return setFromCtor (tor, fields, ctor, TR_FORCE);
905}
906
907static uint64_t
908useFallbackFields (tr_torrent * tor, uint64_t fields, const tr_ctor * ctor)
909{
910  return setFromCtor (tor, fields, ctor, TR_FALLBACK);
911}
912
913uint64_t
914tr_torrentLoadResume (tr_torrent *    tor,
915                      uint64_t        fieldsToLoad,
916                      const tr_ctor * ctor)
917{
918  uint64_t ret = 0;
919
920  assert (tr_isTorrent (tor));
921
922  ret |= useManditoryFields (tor, fieldsToLoad, ctor);
923  fieldsToLoad &= ~ret;
924  ret |= loadFromFile (tor, fieldsToLoad);
925  fieldsToLoad &= ~ret;
926  ret |= useFallbackFields (tor, fieldsToLoad, ctor);
927
928  return ret;
929}
930
931void
932tr_torrentRemoveResume (const tr_torrent * tor)
933{
934  char * filename = getResumeFilename (tor);
935  tr_sys_path_remove (filename, NULL);
936  tr_free (filename);
937}
Note: See TracBrowser for help on using the repository browser.