source: trunk/libtransmission/resume.c @ 13672

Last change on this file since 13672 was 13672, checked in by jordan, 9 years ago

(trunk, libT) faster JSON parsing for tr_variant. This mostly helps the Qt client, which makes heavy use of the JSON-based RPC calls.

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