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