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