source: trunk/libtransmission/resume.c @ 13667

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

refactor libtransmission's tr_benc class as tr_variant.

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