source: trunk/libtransmission/resume.c @ 13868

Last change on this file since 13868 was 13868, checked in by jordan, 8 years ago

make all the log functions/structs/enums use a single 'tr_log' namespace, such as tr_logGetQueue, tr_logAddInfo, tr_logIsLevelActive

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