source: trunk/libtransmission/resume.c @ 13807

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

(trunk) #1220 'change top folder names' -- added support for this in libtransmission's C and RPC APIs.

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