1 /*
2  * This file Copyright (C) 2009-2017 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 <errno.h> /* EINVAL */
10 #include <signal.h> /* signal() */
11 
12 #ifndef _WIN32
13 #include <sys/wait.h> /* wait() */
14 #include <unistd.h> /* fork(), execvp(), _exit() */
15 #else
16 #include <windows.h> /* CreateProcess(), GetLastError() */
17 #endif
18 
19 #include <math.h>
20 #include <stdarg.h>
21 #include <string.h> /* memcmp */
22 #include <stdlib.h> /* qsort */
23 #include <limits.h> /* INT_MAX */
24 
25 #include <event2/util.h> /* evutil_vsnprintf() */
26 
27 #include "transmission.h"
28 #include "announcer.h"
29 #include "bandwidth.h"
30 #include "cache.h"
31 #include "completion.h"
32 #include "crypto-utils.h" /* for tr_sha1 */
33 #include "error.h"
34 #include "fdlimit.h" /* tr_fdTorrentClose */
35 #include "file.h"
36 #include "inout.h" /* tr_ioTestPiece() */
37 #include "log.h"
38 #include "magnet.h"
39 #include "metainfo.h"
40 #include "peer-common.h" /* MAX_BLOCK_SIZE */
41 #include "peer-mgr.h"
42 #include "platform.h" /* TR_PATH_DELIMITER_STR */
43 #include "ptrarray.h"
44 #include "resume.h"
45 #include "session.h"
46 #include "subprocess.h"
47 #include "torrent.h"
48 #include "torrent-magnet.h"
49 #include "tr-assert.h"
50 #include "trevent.h" /* tr_runInEventThread() */
51 #include "utils.h"
52 #include "variant.h"
53 #include "verify.h"
54 #include "version.h"
55 
56 /***
57 ****
58 ***/
59 
60 #define tr_deeplog_tor(tor, ...) tr_logAddDeepNamed(tr_torrentName(tor), __VA_ARGS__)
61 
62 /***
63 ****
64 ***/
65 
tr_torrentName(tr_torrent const * tor)66 char const* tr_torrentName(tr_torrent const* tor)
67 {
68     return tor != NULL ? tor->info.name : "";
69 }
70 
tr_torrentId(tr_torrent const * tor)71 int tr_torrentId(tr_torrent const* tor)
72 {
73     return tor != NULL ? tor->uniqueId : -1;
74 }
75 
tr_torrentFindFromId(tr_session * session,int id)76 tr_torrent* tr_torrentFindFromId(tr_session* session, int id)
77 {
78     tr_torrent* tor = NULL;
79 
80     while ((tor = tr_torrentNext(session, tor)) != NULL)
81     {
82         if (tor->uniqueId == id)
83         {
84             return tor;
85         }
86     }
87 
88     return NULL;
89 }
90 
tr_torrentFindFromHashString(tr_session * session,char const * str)91 tr_torrent* tr_torrentFindFromHashString(tr_session* session, char const* str)
92 {
93     tr_torrent* tor = NULL;
94 
95     while ((tor = tr_torrentNext(session, tor)) != NULL)
96     {
97         if (evutil_ascii_strcasecmp(str, tor->info.hashString) == 0)
98         {
99             return tor;
100         }
101     }
102 
103     return NULL;
104 }
105 
tr_torrentFindFromHash(tr_session * session,uint8_t const * torrentHash)106 tr_torrent* tr_torrentFindFromHash(tr_session* session, uint8_t const* torrentHash)
107 {
108     tr_torrent* tor = NULL;
109 
110     while ((tor = tr_torrentNext(session, tor)) != NULL)
111     {
112         if (*tor->info.hash == *torrentHash)
113         {
114             if (memcmp(tor->info.hash, torrentHash, SHA_DIGEST_LENGTH) == 0)
115             {
116                 return tor;
117             }
118         }
119     }
120 
121     return NULL;
122 }
123 
tr_torrentFindFromMagnetLink(tr_session * session,char const * magnet)124 tr_torrent* tr_torrentFindFromMagnetLink(tr_session* session, char const* magnet)
125 {
126     tr_magnet_info* info;
127     tr_torrent* tor = NULL;
128 
129     if ((info = tr_magnetParse(magnet)) != NULL)
130     {
131         tor = tr_torrentFindFromHash(session, info->hash);
132         tr_magnetFree(info);
133     }
134 
135     return tor;
136 }
137 
tr_torrentFindFromObfuscatedHash(tr_session * session,uint8_t const * obfuscatedTorrentHash)138 tr_torrent* tr_torrentFindFromObfuscatedHash(tr_session* session, uint8_t const* obfuscatedTorrentHash)
139 {
140     tr_torrent* tor = NULL;
141 
142     while ((tor = tr_torrentNext(session, tor)) != NULL)
143     {
144         if (memcmp(tor->obfuscatedHash, obfuscatedTorrentHash, SHA_DIGEST_LENGTH) == 0)
145         {
146             return tor;
147         }
148     }
149 
150     return NULL;
151 }
152 
tr_torrentIsPieceTransferAllowed(tr_torrent const * tor,tr_direction direction)153 bool tr_torrentIsPieceTransferAllowed(tr_torrent const* tor, tr_direction direction)
154 {
155     TR_ASSERT(tr_isTorrent(tor));
156     TR_ASSERT(tr_isDirection(direction));
157 
158     bool allowed = true;
159 
160     if (tr_torrentUsesSpeedLimit(tor, direction))
161     {
162         if (tr_torrentGetSpeedLimit_Bps(tor, direction) <= 0)
163         {
164             allowed = false;
165         }
166     }
167 
168     if (tr_torrentUsesSessionLimits(tor))
169     {
170         unsigned int limit;
171 
172         if (tr_sessionGetActiveSpeedLimit_Bps(tor->session, direction, &limit))
173         {
174             if (limit <= 0)
175             {
176                 allowed = false;
177             }
178         }
179     }
180 
181     return allowed;
182 }
183 
184 /***
185 ****
186 ***/
187 
tr_torrentUnsetPeerId(tr_torrent * tor)188 static void tr_torrentUnsetPeerId(tr_torrent* tor)
189 {
190     /* triggers a rebuild next time tr_torrentGetPeerId() is called */
191     *tor->peer_id = '\0';
192 }
193 
peerIdTTL(tr_torrent const * tor)194 static int peerIdTTL(tr_torrent const* tor)
195 {
196     int ttl;
197 
198     if (tor->peer_id_creation_time == 0)
199     {
200         ttl = 0;
201     }
202     else
203     {
204         ttl = (int)difftime(tor->peer_id_creation_time + tor->session->peer_id_ttl_hours * 3600, tr_time());
205     }
206 
207     return ttl;
208 }
209 
tr_torrentGetPeerId(tr_torrent * tor)210 unsigned char const* tr_torrentGetPeerId(tr_torrent* tor)
211 {
212     bool needs_new_peer_id = false;
213 
214     if (*tor->peer_id == '\0')
215     {
216         needs_new_peer_id = true;
217     }
218 
219     if (!needs_new_peer_id)
220     {
221         if (!tr_torrentIsPrivate(tor))
222         {
223             if (peerIdTTL(tor) <= 0)
224             {
225                 needs_new_peer_id = true;
226             }
227         }
228     }
229 
230     if (needs_new_peer_id)
231     {
232         tr_peerIdInit(tor->peer_id);
233         tor->peer_id_creation_time = tr_time();
234     }
235 
236     return tor->peer_id;
237 }
238 
239 /***
240 ****  PER-TORRENT UL / DL SPEEDS
241 ***/
242 
tr_torrentSetSpeedLimit_Bps(tr_torrent * tor,tr_direction dir,unsigned int Bps)243 void tr_torrentSetSpeedLimit_Bps(tr_torrent* tor, tr_direction dir, unsigned int Bps)
244 {
245     TR_ASSERT(tr_isTorrent(tor));
246     TR_ASSERT(tr_isDirection(dir));
247 
248     if (tr_bandwidthSetDesiredSpeed_Bps(&tor->bandwidth, dir, Bps))
249     {
250         tr_torrentSetDirty(tor);
251     }
252 }
253 
tr_torrentSetSpeedLimit_KBps(tr_torrent * tor,tr_direction dir,unsigned int KBps)254 void tr_torrentSetSpeedLimit_KBps(tr_torrent* tor, tr_direction dir, unsigned int KBps)
255 {
256     tr_torrentSetSpeedLimit_Bps(tor, dir, toSpeedBytes(KBps));
257 }
258 
tr_torrentGetSpeedLimit_Bps(tr_torrent const * tor,tr_direction dir)259 unsigned int tr_torrentGetSpeedLimit_Bps(tr_torrent const* tor, tr_direction dir)
260 {
261     TR_ASSERT(tr_isTorrent(tor));
262     TR_ASSERT(tr_isDirection(dir));
263 
264     return tr_bandwidthGetDesiredSpeed_Bps(&tor->bandwidth, dir);
265 }
266 
tr_torrentGetSpeedLimit_KBps(tr_torrent const * tor,tr_direction dir)267 unsigned int tr_torrentGetSpeedLimit_KBps(tr_torrent const* tor, tr_direction dir)
268 {
269     TR_ASSERT(tr_isTorrent(tor));
270     TR_ASSERT(tr_isDirection(dir));
271 
272     return toSpeedKBps(tr_torrentGetSpeedLimit_Bps(tor, dir));
273 }
274 
tr_torrentUseSpeedLimit(tr_torrent * tor,tr_direction dir,bool do_use)275 void tr_torrentUseSpeedLimit(tr_torrent* tor, tr_direction dir, bool do_use)
276 {
277     TR_ASSERT(tr_isTorrent(tor));
278     TR_ASSERT(tr_isDirection(dir));
279 
280     if (tr_bandwidthSetLimited(&tor->bandwidth, dir, do_use))
281     {
282         tr_torrentSetDirty(tor);
283     }
284 }
285 
tr_torrentUsesSpeedLimit(tr_torrent const * tor,tr_direction dir)286 bool tr_torrentUsesSpeedLimit(tr_torrent const* tor, tr_direction dir)
287 {
288     TR_ASSERT(tr_isTorrent(tor));
289 
290     return tr_bandwidthIsLimited(&tor->bandwidth, dir);
291 }
292 
tr_torrentUseSessionLimits(tr_torrent * tor,bool doUse)293 void tr_torrentUseSessionLimits(tr_torrent* tor, bool doUse)
294 {
295     TR_ASSERT(tr_isTorrent(tor));
296 
297     bool changed;
298 
299     changed = tr_bandwidthHonorParentLimits(&tor->bandwidth, TR_UP, doUse);
300     changed |= tr_bandwidthHonorParentLimits(&tor->bandwidth, TR_DOWN, doUse);
301 
302     if (changed)
303     {
304         tr_torrentSetDirty(tor);
305     }
306 }
307 
tr_torrentUsesSessionLimits(tr_torrent const * tor)308 bool tr_torrentUsesSessionLimits(tr_torrent const* tor)
309 {
310     TR_ASSERT(tr_isTorrent(tor));
311 
312     return tr_bandwidthAreParentLimitsHonored(&tor->bandwidth, TR_UP);
313 }
314 
315 /***
316 ****
317 ***/
318 
tr_torrentSetRatioMode(tr_torrent * tor,tr_ratiolimit mode)319 void tr_torrentSetRatioMode(tr_torrent* tor, tr_ratiolimit mode)
320 {
321     TR_ASSERT(tr_isTorrent(tor));
322     TR_ASSERT(mode == TR_RATIOLIMIT_GLOBAL || mode == TR_RATIOLIMIT_SINGLE || mode == TR_RATIOLIMIT_UNLIMITED);
323 
324     if (mode != tor->ratioLimitMode)
325     {
326         tor->ratioLimitMode = mode;
327 
328         tr_torrentSetDirty(tor);
329     }
330 }
331 
tr_torrentGetRatioMode(tr_torrent const * tor)332 tr_ratiolimit tr_torrentGetRatioMode(tr_torrent const* tor)
333 {
334     TR_ASSERT(tr_isTorrent(tor));
335 
336     return tor->ratioLimitMode;
337 }
338 
tr_torrentSetRatioLimit(tr_torrent * tor,double desiredRatio)339 void tr_torrentSetRatioLimit(tr_torrent* tor, double desiredRatio)
340 {
341     TR_ASSERT(tr_isTorrent(tor));
342 
343     if ((int)(desiredRatio * 100.0) != (int)(tor->desiredRatio * 100.0))
344     {
345         tor->desiredRatio = desiredRatio;
346 
347         tr_torrentSetDirty(tor);
348     }
349 }
350 
tr_torrentGetRatioLimit(tr_torrent const * tor)351 double tr_torrentGetRatioLimit(tr_torrent const* tor)
352 {
353     TR_ASSERT(tr_isTorrent(tor));
354 
355     return tor->desiredRatio;
356 }
357 
tr_torrentGetSeedRatio(tr_torrent const * tor,double * ratio)358 bool tr_torrentGetSeedRatio(tr_torrent const* tor, double* ratio)
359 {
360     TR_ASSERT(tr_isTorrent(tor));
361 
362     bool isLimited;
363 
364     switch (tr_torrentGetRatioMode(tor))
365     {
366     case TR_RATIOLIMIT_SINGLE:
367         isLimited = true;
368 
369         if (ratio != NULL)
370         {
371             *ratio = tr_torrentGetRatioLimit(tor);
372         }
373 
374         break;
375 
376     case TR_RATIOLIMIT_GLOBAL:
377         isLimited = tr_sessionIsRatioLimited(tor->session);
378 
379         if (isLimited && ratio != NULL)
380         {
381             *ratio = tr_sessionGetRatioLimit(tor->session);
382         }
383 
384         break;
385 
386     default: /* TR_RATIOLIMIT_UNLIMITED */
387         isLimited = false;
388         break;
389     }
390 
391     return isLimited;
392 }
393 
394 /* returns true if the seed ratio applies --
395  * it applies if the torrent's a seed AND it has a seed ratio set */
tr_torrentGetSeedRatioBytes(tr_torrent const * tor,uint64_t * setmeLeft,uint64_t * setmeGoal)396 static bool tr_torrentGetSeedRatioBytes(tr_torrent const* tor, uint64_t* setmeLeft, uint64_t* setmeGoal)
397 {
398     TR_ASSERT(tr_isTorrent(tor));
399 
400     double seedRatio;
401     bool seedRatioApplies = false;
402 
403     if (tr_torrentGetSeedRatio(tor, &seedRatio))
404     {
405         uint64_t const u = tor->uploadedCur + tor->uploadedPrev;
406         uint64_t const d = tor->downloadedCur + tor->downloadedPrev;
407         uint64_t const baseline = d != 0 ? d : tr_cpSizeWhenDone(&tor->completion);
408         uint64_t const goal = baseline * seedRatio;
409 
410         if (setmeLeft != NULL)
411         {
412             *setmeLeft = goal > u ? goal - u : 0;
413         }
414 
415         if (setmeGoal != NULL)
416         {
417             *setmeGoal = goal;
418         }
419 
420         seedRatioApplies = tr_torrentIsSeed(tor);
421     }
422 
423     return seedRatioApplies;
424 }
425 
tr_torrentIsSeedRatioDone(tr_torrent const * tor)426 static bool tr_torrentIsSeedRatioDone(tr_torrent const* tor)
427 {
428     uint64_t bytesLeft;
429     return tr_torrentGetSeedRatioBytes(tor, &bytesLeft, NULL) && bytesLeft == 0;
430 }
431 
432 /***
433 ****
434 ***/
435 
tr_torrentSetIdleMode(tr_torrent * tor,tr_idlelimit mode)436 void tr_torrentSetIdleMode(tr_torrent* tor, tr_idlelimit mode)
437 {
438     TR_ASSERT(tr_isTorrent(tor));
439     TR_ASSERT(mode == TR_IDLELIMIT_GLOBAL || mode == TR_IDLELIMIT_SINGLE || mode == TR_IDLELIMIT_UNLIMITED);
440 
441     if (mode != tor->idleLimitMode)
442     {
443         tor->idleLimitMode = mode;
444 
445         tr_torrentSetDirty(tor);
446     }
447 }
448 
tr_torrentGetIdleMode(tr_torrent const * tor)449 tr_idlelimit tr_torrentGetIdleMode(tr_torrent const* tor)
450 {
451     TR_ASSERT(tr_isTorrent(tor));
452 
453     return tor->idleLimitMode;
454 }
455 
tr_torrentSetIdleLimit(tr_torrent * tor,uint16_t idleMinutes)456 void tr_torrentSetIdleLimit(tr_torrent* tor, uint16_t idleMinutes)
457 {
458     TR_ASSERT(tr_isTorrent(tor));
459 
460     if (idleMinutes > 0)
461     {
462         tor->idleLimitMinutes = idleMinutes;
463 
464         tr_torrentSetDirty(tor);
465     }
466 }
467 
tr_torrentGetIdleLimit(tr_torrent const * tor)468 uint16_t tr_torrentGetIdleLimit(tr_torrent const* tor)
469 {
470     TR_ASSERT(tr_isTorrent(tor));
471 
472     return tor->idleLimitMinutes;
473 }
474 
tr_torrentGetSeedIdle(tr_torrent const * tor,uint16_t * idleMinutes)475 bool tr_torrentGetSeedIdle(tr_torrent const* tor, uint16_t* idleMinutes)
476 {
477     bool isLimited;
478 
479     switch (tr_torrentGetIdleMode(tor))
480     {
481     case TR_IDLELIMIT_SINGLE:
482         isLimited = true;
483 
484         if (idleMinutes != NULL)
485         {
486             *idleMinutes = tr_torrentGetIdleLimit(tor);
487         }
488 
489         break;
490 
491     case TR_IDLELIMIT_GLOBAL:
492         isLimited = tr_sessionIsIdleLimited(tor->session);
493 
494         if (isLimited && idleMinutes != NULL)
495         {
496             *idleMinutes = tr_sessionGetIdleLimit(tor->session);
497         }
498 
499         break;
500 
501     default: /* TR_IDLELIMIT_UNLIMITED */
502         isLimited = false;
503         break;
504     }
505 
506     return isLimited;
507 }
508 
tr_torrentIsSeedIdleLimitDone(tr_torrent * tor)509 static bool tr_torrentIsSeedIdleLimitDone(tr_torrent* tor)
510 {
511     uint16_t idleMinutes;
512     return tr_torrentGetSeedIdle(tor, &idleMinutes) &&
513         difftime(tr_time(), MAX(tor->startDate, tor->activityDate)) >= idleMinutes * 60U;
514 }
515 
516 /***
517 ****
518 ***/
519 
tr_torrentCheckSeedLimit(tr_torrent * tor)520 void tr_torrentCheckSeedLimit(tr_torrent* tor)
521 {
522     TR_ASSERT(tr_isTorrent(tor));
523 
524     if (!tor->isRunning || tor->isStopping || !tr_torrentIsSeed(tor))
525     {
526         return;
527     }
528 
529     /* if we're seeding and reach our seed ratio limit, stop the torrent */
530     if (tr_torrentIsSeedRatioDone(tor))
531     {
532         tr_logAddTorInfo(tor, "%s", "Seed ratio reached; pausing torrent");
533 
534         tor->isStopping = true;
535 
536         /* maybe notify the client */
537         if (tor->ratio_limit_hit_func != NULL)
538         {
539             (*tor->ratio_limit_hit_func)(tor, tor->ratio_limit_hit_func_user_data);
540         }
541     }
542     /* if we're seeding and reach our inactiviy limit, stop the torrent */
543     else if (tr_torrentIsSeedIdleLimitDone(tor))
544     {
545         tr_logAddTorInfo(tor, "%s", "Seeding idle limit reached; pausing torrent");
546 
547         tor->isStopping = true;
548         tor->finishedSeedingByIdle = true;
549 
550         /* maybe notify the client */
551         if (tor->idle_limit_hit_func != NULL)
552         {
553             (*tor->idle_limit_hit_func)(tor, tor->idle_limit_hit_func_user_data);
554         }
555     }
556 }
557 
558 /***
559 ****
560 ***/
561 
tr_torrentSetLocalError(tr_torrent * tor,char const * fmt,...)562 void tr_torrentSetLocalError(tr_torrent* tor, char const* fmt, ...)
563 {
564     TR_ASSERT(tr_isTorrent(tor));
565 
566     va_list ap;
567 
568     va_start(ap, fmt);
569     tor->error = TR_STAT_LOCAL_ERROR;
570     tor->errorTracker[0] = '\0';
571     evutil_vsnprintf(tor->errorString, sizeof(tor->errorString), fmt, ap);
572     va_end(ap);
573 
574     tr_logAddTorErr(tor, "%s", tor->errorString);
575 
576     if (tor->isRunning)
577     {
578         tor->isStopping = true;
579     }
580 }
581 
tr_torrentClearError(tr_torrent * tor)582 static void tr_torrentClearError(tr_torrent* tor)
583 {
584     tor->error = TR_STAT_OK;
585     tor->errorString[0] = '\0';
586     tor->errorTracker[0] = '\0';
587 }
588 
onTrackerResponse(tr_torrent * tor,tr_tracker_event const * event,void * unused UNUSED)589 static void onTrackerResponse(tr_torrent* tor, tr_tracker_event const* event, void* unused UNUSED)
590 {
591     switch (event->messageType)
592     {
593     case TR_TRACKER_PEERS:
594         {
595             int8_t const seedProbability = event->seedProbability;
596             bool const allAreSeeds = seedProbability == 100;
597 
598             if (allAreSeeds)
599             {
600                 tr_logAddTorDbg(tor, "Got %zu seeds from tracker", event->pexCount);
601             }
602             else
603             {
604                 tr_logAddTorDbg(tor, "Got %zu peers from tracker", event->pexCount);
605             }
606 
607             for (size_t i = 0; i < event->pexCount; ++i)
608             {
609                 tr_peerMgrAddPex(tor, TR_PEER_FROM_TRACKER, &event->pex[i], seedProbability);
610             }
611 
612             break;
613         }
614 
615     case TR_TRACKER_WARNING:
616         tr_logAddTorErr(tor, _("Tracker warning: \"%s\""), event->text);
617         tor->error = TR_STAT_TRACKER_WARNING;
618         tr_strlcpy(tor->errorTracker, event->tracker, sizeof(tor->errorTracker));
619         tr_strlcpy(tor->errorString, event->text, sizeof(tor->errorString));
620         break;
621 
622     case TR_TRACKER_ERROR:
623         tr_logAddTorErr(tor, _("Tracker error: \"%s\""), event->text);
624         tor->error = TR_STAT_TRACKER_ERROR;
625         tr_strlcpy(tor->errorTracker, event->tracker, sizeof(tor->errorTracker));
626         tr_strlcpy(tor->errorString, event->text, sizeof(tor->errorString));
627         break;
628 
629     case TR_TRACKER_ERROR_CLEAR:
630         if (tor->error != TR_STAT_LOCAL_ERROR)
631         {
632             tr_torrentClearError(tor);
633         }
634 
635         break;
636     }
637 }
638 
639 /***
640 ****
641 ****  TORRENT INSTANTIATION
642 ****
643 ***/
644 
getBytePiece(tr_info const * info,uint64_t byteOffset)645 static tr_piece_index_t getBytePiece(tr_info const* info, uint64_t byteOffset)
646 {
647     TR_ASSERT(info != NULL);
648     TR_ASSERT(info->pieceSize != 0);
649 
650     tr_piece_index_t piece = byteOffset / info->pieceSize;
651 
652     /* handle 0-byte files at the end of a torrent */
653     if (byteOffset == info->totalSize)
654     {
655         piece = info->pieceCount - 1;
656     }
657 
658     return piece;
659 }
660 
initFilePieces(tr_info * info,tr_file_index_t fileIndex)661 static void initFilePieces(tr_info* info, tr_file_index_t fileIndex)
662 {
663     TR_ASSERT(info != NULL);
664     TR_ASSERT(fileIndex < info->fileCount);
665 
666     tr_file* file = &info->files[fileIndex];
667     uint64_t firstByte = file->offset;
668     uint64_t lastByte = firstByte + (file->length != 0 ? file->length - 1 : 0);
669 
670     file->firstPiece = getBytePiece(info, firstByte);
671     file->lastPiece = getBytePiece(info, lastByte);
672 }
673 
pieceHasFile(tr_piece_index_t piece,tr_file const * file)674 static bool pieceHasFile(tr_piece_index_t piece, tr_file const* file)
675 {
676     return file->firstPiece <= piece && piece <= file->lastPiece;
677 }
678 
calculatePiecePriority(tr_torrent const * tor,tr_piece_index_t piece,int fileHint)679 static tr_priority_t calculatePiecePriority(tr_torrent const* tor, tr_piece_index_t piece, int fileHint)
680 {
681     tr_file_index_t firstFileIndex;
682     tr_priority_t priority = TR_PRI_LOW;
683 
684     /* find the first file that has data in this piece */
685     if (fileHint >= 0)
686     {
687         firstFileIndex = fileHint;
688 
689         while (firstFileIndex > 0 && pieceHasFile(piece, &tor->info.files[firstFileIndex - 1]))
690         {
691             --firstFileIndex;
692         }
693     }
694     else
695     {
696         firstFileIndex = 0;
697 
698         for (tr_file_index_t i = 0; i < tor->info.fileCount; ++i, ++firstFileIndex)
699         {
700             if (pieceHasFile(piece, &tor->info.files[i]))
701             {
702                 break;
703             }
704         }
705     }
706 
707     /* the piece's priority is the max of the priorities
708      * of all the files in that piece */
709     for (tr_file_index_t i = firstFileIndex; i < tor->info.fileCount; ++i)
710     {
711         tr_file const* file = &tor->info.files[i];
712 
713         if (!pieceHasFile(piece, file))
714         {
715             break;
716         }
717 
718         priority = MAX(priority, file->priority);
719 
720         /* when dealing with multimedia files, getting the first and
721            last pieces can sometimes allow you to preview it a bit
722            before it's fully downloaded... */
723         if (file->priority >= TR_PRI_NORMAL)
724         {
725             if (file->firstPiece == piece || file->lastPiece == piece)
726             {
727                 priority = TR_PRI_HIGH;
728             }
729         }
730     }
731 
732     return priority;
733 }
734 
tr_torrentInitFilePieces(tr_torrent * tor)735 static void tr_torrentInitFilePieces(tr_torrent* tor)
736 {
737     uint64_t offset = 0;
738     tr_info* inf = &tor->info;
739 
740     /* assign the file offsets */
741     for (tr_file_index_t f = 0; f < inf->fileCount; ++f)
742     {
743         inf->files[f].offset = offset;
744         offset += inf->files[f].length;
745         initFilePieces(inf, f);
746     }
747 
748     /* build the array of first-file hints to give calculatePiecePriority */
749     int* firstFiles = tr_new(int, inf->pieceCount);
750     tr_file_index_t f = 0;
751 
752     for (tr_piece_index_t p = 0; p < inf->pieceCount; ++p)
753     {
754         while (inf->files[f].lastPiece < p)
755         {
756             ++f;
757         }
758 
759         firstFiles[p] = f;
760     }
761 
762 #if 0
763 
764     /* test to confirm the first-file hints are correct */
765     for (tr_piece_index_t p = 0; p < inf->pieceCount; ++p)
766     {
767         tr_file_index_t f = firstFiles[p];
768 
769         TR_ASSERT(inf->files[f].firstPiece <= p);
770         TR_ASSERT(inf->files[f].lastPiece >= p);
771 
772         if (f > 0)
773         {
774             TR_ASSERT(inf->files[f - 1].lastPiece < p);
775         }
776 
777         f = 0;
778 
779         for (tr_file_index_t i = 0; i < inf->fileCount; ++i, ++f)
780         {
781             if (pieceHasFile(p, &inf->files[i]))
782             {
783                 break;
784             }
785         }
786 
787         TR_ASSERT((int)f == firstFiles[p]);
788     }
789 
790 #endif
791 
792     for (tr_piece_index_t p = 0; p < inf->pieceCount; ++p)
793     {
794         inf->pieces[p].priority = calculatePiecePriority(tor, p, firstFiles[p]);
795     }
796 
797     tr_free(firstFiles);
798 }
799 
800 static void torrentStart(tr_torrent* tor, bool bypass_queue);
801 
802 /**
803  * Decide on a block size. Constraints:
804  * (1) most clients decline requests over 16 KiB
805  * (2) pieceSize must be a multiple of block size
806  */
tr_getBlockSize(uint32_t pieceSize)807 uint32_t tr_getBlockSize(uint32_t pieceSize)
808 {
809     uint32_t b = pieceSize;
810 
811     while (b > MAX_BLOCK_SIZE)
812     {
813         b /= 2U;
814     }
815 
816     if (b == 0 || pieceSize % b != 0) /* not cleanly divisible */
817     {
818         return 0;
819     }
820 
821     return b;
822 }
823 
824 static void refreshCurrentDir(tr_torrent* tor);
825 
torrentInitFromInfo(tr_torrent * tor)826 static void torrentInitFromInfo(tr_torrent* tor)
827 {
828     uint64_t t;
829     tr_info* info = &tor->info;
830 
831     tor->blockSize = tr_getBlockSize(info->pieceSize);
832 
833     if (info->pieceSize != 0)
834     {
835         tor->lastPieceSize = (uint32_t)(info->totalSize % info->pieceSize);
836     }
837 
838     if (tor->lastPieceSize == 0)
839     {
840         tor->lastPieceSize = info->pieceSize;
841     }
842 
843     if (tor->blockSize != 0)
844     {
845         tor->lastBlockSize = info->totalSize % tor->blockSize;
846     }
847 
848     if (tor->lastBlockSize == 0)
849     {
850         tor->lastBlockSize = tor->blockSize;
851     }
852 
853     tor->blockCount = tor->blockSize != 0 ? (info->totalSize + tor->blockSize - 1) / tor->blockSize : 0;
854     tor->blockCountInPiece = tor->blockSize != 0 ? info->pieceSize / tor->blockSize : 0;
855     tor->blockCountInLastPiece = tor->blockSize != 0 ? (tor->lastPieceSize + tor->blockSize - 1) / tor->blockSize : 0;
856 
857     /* check our work */
858     if (tor->blockSize != 0)
859     {
860         TR_ASSERT(info->pieceSize % tor->blockSize == 0);
861     }
862 
863     t = info->pieceCount - 1;
864     t *= info->pieceSize;
865     t += tor->lastPieceSize;
866     TR_ASSERT(t == info->totalSize);
867 
868     t = tor->blockCount - 1;
869     t *= tor->blockSize;
870     t += tor->lastBlockSize;
871     TR_ASSERT(t == info->totalSize);
872 
873     t = info->pieceCount - 1;
874     t *= tor->blockCountInPiece;
875     t += tor->blockCountInLastPiece;
876     TR_ASSERT(t == (uint64_t)tor->blockCount);
877 
878     tr_cpConstruct(&tor->completion, tor);
879 
880     tr_torrentInitFilePieces(tor);
881 
882     tor->completeness = tr_cpGetStatus(&tor->completion);
883 }
884 
885 static void tr_torrentFireMetadataCompleted(tr_torrent* tor);
886 
tr_torrentGotNewInfoDict(tr_torrent * tor)887 void tr_torrentGotNewInfoDict(tr_torrent* tor)
888 {
889     torrentInitFromInfo(tor);
890 
891     tr_peerMgrOnTorrentGotMetainfo(tor);
892 
893     tr_torrentFireMetadataCompleted(tor);
894 }
895 
hasAnyLocalData(tr_torrent const * tor)896 static bool hasAnyLocalData(tr_torrent const* tor)
897 {
898     for (tr_file_index_t i = 0; i < tor->info.fileCount; ++i)
899     {
900         if (tr_torrentFindFile2(tor, i, NULL, NULL, NULL))
901         {
902             return true;
903         }
904     }
905 
906     return false;
907 }
908 
setLocalErrorIfFilesDisappeared(tr_torrent * tor)909 static bool setLocalErrorIfFilesDisappeared(tr_torrent* tor)
910 {
911     bool const disappeared = tr_torrentHaveTotal(tor) > 0 && !hasAnyLocalData(tor);
912 
913     if (disappeared)
914     {
915         tr_deeplog_tor(tor, "%s", "[LAZY] uh oh, the files disappeared");
916         tr_torrentSetLocalError(tor, "%s", _("No data found! Ensure your drives are connected or use \"Set Location\". "
917             "To re-download, remove the torrent and re-add it."));
918     }
919 
920     return disappeared;
921 }
922 
torrentInit(tr_torrent * tor,tr_ctor const * ctor)923 static void torrentInit(tr_torrent* tor, tr_ctor const* ctor)
924 {
925     tr_session* session = tr_ctorGetSession(ctor);
926 
927     TR_ASSERT(session != NULL);
928 
929     tr_sessionLock(session);
930 
931     bool doStart;
932     uint64_t loaded;
933     char const* dir;
934     bool isNewTorrent;
935     static int nextUniqueId = 1;
936 
937     tor->session = session;
938     tor->uniqueId = nextUniqueId++;
939     tor->magicNumber = TORRENT_MAGIC_NUMBER;
940     tor->queuePosition = session->torrentCount;
941     tor->labels = TR_PTR_ARRAY_INIT;
942 
943     tr_sha1(tor->obfuscatedHash, "req2", 4, tor->info.hash, SHA_DIGEST_LENGTH, NULL);
944 
945     if (tr_ctorGetDownloadDir(ctor, TR_FORCE, &dir) || tr_ctorGetDownloadDir(ctor, TR_FALLBACK, &dir))
946     {
947         tor->downloadDir = tr_strdup(dir);
948     }
949 
950     if (!tr_ctorGetIncompleteDir(ctor, &dir))
951     {
952         dir = tr_sessionGetIncompleteDir(session);
953     }
954 
955     if (tr_sessionIsIncompleteDirEnabled(session))
956     {
957         tor->incompleteDir = tr_strdup(dir);
958     }
959 
960     tr_bandwidthConstruct(&tor->bandwidth, session, &session->bandwidth);
961 
962     tor->bandwidth.priority = tr_ctorGetBandwidthPriority(ctor);
963     tor->error = TR_STAT_OK;
964     tor->finishedSeedingByIdle = false;
965 
966     tr_peerMgrAddTorrent(session->peerMgr, tor);
967 
968     TR_ASSERT(tor->downloadedCur == 0);
969     TR_ASSERT(tor->uploadedCur == 0);
970 
971     tr_torrentSetDateAdded(tor, tr_time()); /* this is a default value to be overwritten by the resume file */
972 
973     torrentInitFromInfo(tor);
974 
975     bool didRenameResumeFileToHashOnlyName = false;
976     loaded = tr_torrentLoadResume(tor, ~0, ctor, &didRenameResumeFileToHashOnlyName);
977 
978     if (didRenameResumeFileToHashOnlyName)
979     {
980         /* Rename torrent file as well */
981         tr_metainfoMigrateFile(session, &tor->info, TR_METAINFO_BASENAME_NAME_AND_PARTIAL_HASH, TR_METAINFO_BASENAME_HASH);
982     }
983 
984     tor->completeness = tr_cpGetStatus(&tor->completion);
985     setLocalErrorIfFilesDisappeared(tor);
986 
987     tr_ctorInitTorrentPriorities(ctor, tor);
988     tr_ctorInitTorrentWanted(ctor, tor);
989 
990     refreshCurrentDir(tor);
991 
992     doStart = tor->isRunning;
993     tor->isRunning = false;
994 
995     if ((loaded & TR_FR_SPEEDLIMIT) == 0)
996     {
997         tr_torrentUseSpeedLimit(tor, TR_UP, false);
998         tr_torrentSetSpeedLimit_Bps(tor, TR_UP, tr_sessionGetSpeedLimit_Bps(tor->session, TR_UP));
999         tr_torrentUseSpeedLimit(tor, TR_DOWN, false);
1000         tr_torrentSetSpeedLimit_Bps(tor, TR_DOWN, tr_sessionGetSpeedLimit_Bps(tor->session, TR_DOWN));
1001         tr_torrentUseSessionLimits(tor, true);
1002     }
1003 
1004     if ((loaded & TR_FR_RATIOLIMIT) == 0)
1005     {
1006         tr_torrentSetRatioMode(tor, TR_RATIOLIMIT_GLOBAL);
1007         tr_torrentSetRatioLimit(tor, tr_sessionGetRatioLimit(tor->session));
1008     }
1009 
1010     if ((loaded & TR_FR_IDLELIMIT) == 0)
1011     {
1012         tr_torrentSetIdleMode(tor, TR_IDLELIMIT_GLOBAL);
1013         tr_torrentSetIdleLimit(tor, tr_sessionGetIdleLimit(tor->session));
1014     }
1015 
1016     /* add the torrent to tr_session.torrentList */
1017     session->torrentCount++;
1018 
1019     if (session->torrentList == NULL)
1020     {
1021         session->torrentList = tor;
1022     }
1023     else
1024     {
1025         tr_torrent* it = session->torrentList;
1026 
1027         while (it->next != NULL)
1028         {
1029             it = it->next;
1030         }
1031 
1032         it->next = tor;
1033     }
1034 
1035     /* if we don't have a local .torrent file already, assume the torrent is new */
1036     isNewTorrent = !tr_sys_path_exists(tor->info.torrent, NULL);
1037 
1038     /* maybe save our own copy of the metainfo */
1039     if (tr_ctorGetSave(ctor))
1040     {
1041         tr_variant const* val;
1042 
1043         if (tr_ctorGetMetainfo(ctor, &val))
1044         {
1045             char const* path = tor->info.torrent;
1046             int const err = tr_variantToFile(val, TR_VARIANT_FMT_BENC, path);
1047 
1048             if (err != 0)
1049             {
1050                 tr_torrentSetLocalError(tor, "Unable to save torrent file: %s", tr_strerror(err));
1051             }
1052 
1053             tr_sessionSetTorrentFile(tor->session, tor->info.hashString, path);
1054         }
1055     }
1056 
1057     tor->tiers = tr_announcerAddTorrent(tor, onTrackerResponse, NULL);
1058 
1059     if (isNewTorrent)
1060     {
1061         tor->startAfterVerify = doStart;
1062         tr_torrentVerify(tor, NULL, NULL);
1063     }
1064     else if (doStart)
1065     {
1066         tr_torrentStart(tor);
1067     }
1068 
1069     tr_sessionUnlock(session);
1070 }
1071 
torrentParseImpl(tr_ctor const * ctor,tr_info * setmeInfo,bool * setmeHasInfo,size_t * dictLength,int * setme_duplicate_id)1072 static tr_parse_result torrentParseImpl(tr_ctor const* ctor, tr_info* setmeInfo, bool* setmeHasInfo, size_t* dictLength,
1073     int* setme_duplicate_id)
1074 {
1075     bool doFree;
1076     bool didParse;
1077     bool hasInfo = false;
1078     tr_info tmp;
1079     tr_variant const* metainfo;
1080     tr_session* session = tr_ctorGetSession(ctor);
1081     tr_parse_result result = TR_PARSE_OK;
1082 
1083     if (setmeInfo == NULL)
1084     {
1085         setmeInfo = &tmp;
1086     }
1087 
1088     memset(setmeInfo, 0, sizeof(tr_info));
1089 
1090     if (!tr_ctorGetMetainfo(ctor, &metainfo))
1091     {
1092         return TR_PARSE_ERR;
1093     }
1094 
1095     didParse = tr_metainfoParse(session, metainfo, setmeInfo, &hasInfo, dictLength);
1096     doFree = didParse && (setmeInfo == &tmp);
1097 
1098     if (!didParse)
1099     {
1100         result = TR_PARSE_ERR;
1101     }
1102 
1103     if (didParse && hasInfo && tr_getBlockSize(setmeInfo->pieceSize) == 0)
1104     {
1105         result = TR_PARSE_ERR;
1106     }
1107 
1108     if (didParse && session != NULL && result == TR_PARSE_OK)
1109     {
1110         tr_torrent const* const tor = tr_torrentFindFromHash(session, setmeInfo->hash);
1111 
1112         if (tor != NULL)
1113         {
1114             result = TR_PARSE_DUPLICATE;
1115 
1116             if (setme_duplicate_id != NULL)
1117             {
1118                 *setme_duplicate_id = tr_torrentId(tor);
1119             }
1120         }
1121     }
1122 
1123     if (doFree)
1124     {
1125         tr_metainfoFree(setmeInfo);
1126     }
1127 
1128     if (setmeHasInfo != NULL)
1129     {
1130         *setmeHasInfo = hasInfo;
1131     }
1132 
1133     return result;
1134 }
1135 
tr_torrentParse(tr_ctor const * ctor,tr_info * setmeInfo)1136 tr_parse_result tr_torrentParse(tr_ctor const* ctor, tr_info* setmeInfo)
1137 {
1138     return torrentParseImpl(ctor, setmeInfo, NULL, NULL, NULL);
1139 }
1140 
tr_torrentNew(tr_ctor const * ctor,int * setme_error,int * setme_duplicate_id)1141 tr_torrent* tr_torrentNew(tr_ctor const* ctor, int* setme_error, int* setme_duplicate_id)
1142 {
1143     TR_ASSERT(ctor != NULL);
1144     TR_ASSERT(tr_isSession(tr_ctorGetSession(ctor)));
1145 
1146     size_t len;
1147     bool hasInfo;
1148     tr_info tmpInfo;
1149     tr_parse_result r;
1150     tr_torrent* tor = NULL;
1151 
1152     r = torrentParseImpl(ctor, &tmpInfo, &hasInfo, &len, setme_duplicate_id);
1153 
1154     if (r == TR_PARSE_OK)
1155     {
1156         tor = tr_new0(tr_torrent, 1);
1157         tor->info = tmpInfo;
1158 
1159         if (hasInfo)
1160         {
1161             tor->infoDictLength = len;
1162         }
1163 
1164         torrentInit(tor, ctor);
1165     }
1166     else
1167     {
1168         if (r == TR_PARSE_DUPLICATE)
1169         {
1170             tr_metainfoFree(&tmpInfo);
1171         }
1172 
1173         if (setme_error != NULL)
1174         {
1175             *setme_error = r;
1176         }
1177     }
1178 
1179     return tor;
1180 }
1181 
1182 /**
1183 ***
1184 **/
1185 
tr_torrentSetDownloadDir(tr_torrent * tor,char const * path)1186 void tr_torrentSetDownloadDir(tr_torrent* tor, char const* path)
1187 {
1188     TR_ASSERT(tr_isTorrent(tor));
1189 
1190     if (path == NULL || tor->downloadDir == NULL || strcmp(path, tor->downloadDir) != 0)
1191     {
1192         tr_free(tor->downloadDir);
1193         tor->downloadDir = tr_strdup(path);
1194 
1195         tr_torrentMarkEdited(tor);
1196         tr_torrentSetDirty(tor);
1197     }
1198 
1199     refreshCurrentDir(tor);
1200 }
1201 
tr_torrentGetDownloadDir(tr_torrent const * tor)1202 char const* tr_torrentGetDownloadDir(tr_torrent const* tor)
1203 {
1204     TR_ASSERT(tr_isTorrent(tor));
1205 
1206     return tor->downloadDir;
1207 }
1208 
tr_torrentGetCurrentDir(tr_torrent const * tor)1209 char const* tr_torrentGetCurrentDir(tr_torrent const* tor)
1210 {
1211     TR_ASSERT(tr_isTorrent(tor));
1212 
1213     return tor->currentDir;
1214 }
1215 
tr_torrentChangeMyPort(tr_torrent * tor)1216 void tr_torrentChangeMyPort(tr_torrent* tor)
1217 {
1218     TR_ASSERT(tr_isTorrent(tor));
1219 
1220     if (tor->isRunning)
1221     {
1222         tr_announcerChangeMyPort(tor);
1223     }
1224 }
1225 
tr_torrentManualUpdateImpl(void * vtor)1226 static inline void tr_torrentManualUpdateImpl(void* vtor)
1227 {
1228     tr_torrent* tor = vtor;
1229 
1230     TR_ASSERT(tr_isTorrent(tor));
1231 
1232     if (tor->isRunning)
1233     {
1234         tr_announcerManualAnnounce(tor);
1235     }
1236 }
1237 
tr_torrentManualUpdate(tr_torrent * tor)1238 void tr_torrentManualUpdate(tr_torrent* tor)
1239 {
1240     TR_ASSERT(tr_isTorrent(tor));
1241 
1242     tr_runInEventThread(tor->session, tr_torrentManualUpdateImpl, tor);
1243 }
1244 
tr_torrentCanManualUpdate(tr_torrent const * tor)1245 bool tr_torrentCanManualUpdate(tr_torrent const* tor)
1246 {
1247     return tr_isTorrent(tor) && tor->isRunning && tr_announcerCanManualAnnounce(tor);
1248 }
1249 
tr_torrentInfo(tr_torrent const * tor)1250 tr_info const* tr_torrentInfo(tr_torrent const* tor)
1251 {
1252     return tr_isTorrent(tor) ? &tor->info : NULL;
1253 }
1254 
tr_torrentStatCached(tr_torrent * tor)1255 tr_stat const* tr_torrentStatCached(tr_torrent* tor)
1256 {
1257     time_t const now = tr_time();
1258 
1259     return (tr_isTorrent(tor) && now == tor->lastStatTime) ? &tor->stats : tr_torrentStat(tor);
1260 }
1261 
tr_torrentSetVerifyState(tr_torrent * tor,tr_verify_state state)1262 void tr_torrentSetVerifyState(tr_torrent* tor, tr_verify_state state)
1263 {
1264     TR_ASSERT(tr_isTorrent(tor));
1265     TR_ASSERT(state == TR_VERIFY_NONE || state == TR_VERIFY_WAIT || state == TR_VERIFY_NOW);
1266 
1267     tor->verifyState = state;
1268     tor->anyDate = tr_time();
1269 }
1270 
tr_torrentGetActivity(tr_torrent const * tor)1271 tr_torrent_activity tr_torrentGetActivity(tr_torrent const* tor)
1272 {
1273     tr_torrent_activity ret = TR_STATUS_STOPPED;
1274 
1275     bool const is_seed = tr_torrentIsSeed(tor);
1276 
1277     if (tor->verifyState == TR_VERIFY_NOW)
1278     {
1279         ret = TR_STATUS_CHECK;
1280     }
1281     else if (tor->verifyState == TR_VERIFY_WAIT)
1282     {
1283         ret = TR_STATUS_CHECK_WAIT;
1284     }
1285     else if (tor->isRunning)
1286     {
1287         ret = is_seed ? TR_STATUS_SEED : TR_STATUS_DOWNLOAD;
1288     }
1289     else if (tr_torrentIsQueued(tor))
1290     {
1291         if (is_seed && tr_sessionGetQueueEnabled(tor->session, TR_UP))
1292         {
1293             ret = TR_STATUS_SEED_WAIT;
1294         }
1295         else if (!is_seed && tr_sessionGetQueueEnabled(tor->session, TR_DOWN))
1296         {
1297             ret = TR_STATUS_DOWNLOAD_WAIT;
1298         }
1299     }
1300 
1301     return ret;
1302 }
1303 
torrentGetIdleSecs(tr_torrent const * tor)1304 static time_t torrentGetIdleSecs(tr_torrent const* tor)
1305 {
1306     int idle_secs;
1307     tr_torrent_activity const activity = tr_torrentGetActivity(tor);
1308 
1309     if ((activity == TR_STATUS_DOWNLOAD || activity == TR_STATUS_SEED) && tor->startDate != 0)
1310     {
1311         idle_secs = difftime(tr_time(), MAX(tor->startDate, tor->activityDate));
1312     }
1313     else
1314     {
1315         idle_secs = -1;
1316     }
1317 
1318     return idle_secs;
1319 }
1320 
tr_torrentIsStalled(tr_torrent const * tor)1321 bool tr_torrentIsStalled(tr_torrent const* tor)
1322 {
1323     return tr_sessionGetQueueStalledEnabled(tor->session) &&
1324         torrentGetIdleSecs(tor) > tr_sessionGetQueueStalledMinutes(tor->session) * 60;
1325 }
1326 
getVerifyProgress(tr_torrent const * tor)1327 static double getVerifyProgress(tr_torrent const* tor)
1328 {
1329     double d = 0;
1330 
1331     if (tr_torrentHasMetadata(tor))
1332     {
1333         tr_piece_index_t checked = 0;
1334 
1335         for (tr_piece_index_t i = 0; i < tor->info.pieceCount; ++i)
1336         {
1337             if (tor->info.pieces[i].timeChecked != 0)
1338             {
1339                 ++checked;
1340             }
1341         }
1342 
1343         d = checked / (double)tor->info.pieceCount;
1344     }
1345 
1346     return d;
1347 }
1348 
tr_torrentStat(tr_torrent * tor)1349 tr_stat const* tr_torrentStat(tr_torrent* tor)
1350 {
1351     TR_ASSERT(tr_isTorrent(tor));
1352 
1353     uint64_t const now = tr_time_msec();
1354 
1355     tr_stat* s;
1356     uint64_t seedRatioBytesLeft;
1357     uint64_t seedRatioBytesGoal;
1358     bool seedRatioApplies;
1359     uint16_t seedIdleMinutes;
1360     unsigned int pieceUploadSpeed_Bps;
1361     unsigned int pieceDownloadSpeed_Bps;
1362     struct tr_swarm_stats swarm_stats;
1363 
1364     tor->lastStatTime = tr_time();
1365 
1366     if (tor->swarm != NULL)
1367     {
1368         tr_swarmGetStats(tor->swarm, &swarm_stats);
1369     }
1370     else
1371     {
1372         swarm_stats = TR_SWARM_STATS_INIT;
1373     }
1374 
1375     s = &tor->stats;
1376     s->id = tor->uniqueId;
1377     s->activity = tr_torrentGetActivity(tor);
1378     s->error = tor->error;
1379     s->queuePosition = tor->queuePosition;
1380     s->isStalled = tr_torrentIsStalled(tor);
1381     tr_strlcpy(s->errorString, tor->errorString, sizeof(s->errorString));
1382 
1383     s->manualAnnounceTime = tr_announcerNextManualAnnounce(tor);
1384     s->peersConnected = swarm_stats.peerCount;
1385     s->peersSendingToUs = swarm_stats.activePeerCount[TR_DOWN];
1386     s->peersGettingFromUs = swarm_stats.activePeerCount[TR_UP];
1387     s->webseedsSendingToUs = swarm_stats.activeWebseedCount;
1388 
1389     for (int i = 0; i < TR_PEER_FROM__MAX; i++)
1390     {
1391         s->peersFrom[i] = swarm_stats.peerFromCount[i];
1392     }
1393 
1394     s->rawUploadSpeed_KBps = toSpeedKBps(tr_bandwidthGetRawSpeed_Bps(&tor->bandwidth, now, TR_UP));
1395     s->rawDownloadSpeed_KBps = toSpeedKBps(tr_bandwidthGetRawSpeed_Bps(&tor->bandwidth, now, TR_DOWN));
1396     pieceUploadSpeed_Bps = tr_bandwidthGetPieceSpeed_Bps(&tor->bandwidth, now, TR_UP);
1397     pieceDownloadSpeed_Bps = tr_bandwidthGetPieceSpeed_Bps(&tor->bandwidth, now, TR_DOWN);
1398     s->pieceUploadSpeed_KBps = toSpeedKBps(pieceUploadSpeed_Bps);
1399     s->pieceDownloadSpeed_KBps = toSpeedKBps(pieceDownloadSpeed_Bps);
1400 
1401     s->percentComplete = tr_cpPercentComplete(&tor->completion);
1402     s->metadataPercentComplete = tr_torrentGetMetadataPercent(tor);
1403 
1404     s->percentDone = tr_cpPercentDone(&tor->completion);
1405     s->leftUntilDone = tr_torrentGetLeftUntilDone(tor);
1406     s->sizeWhenDone = tr_cpSizeWhenDone(&tor->completion);
1407     s->recheckProgress = s->activity == TR_STATUS_CHECK ? getVerifyProgress(tor) : 0;
1408     s->activityDate = tor->activityDate;
1409     s->addedDate = tor->addedDate;
1410     s->doneDate = tor->doneDate;
1411     s->editDate = tor->editDate;
1412     s->startDate = tor->startDate;
1413     s->secondsSeeding = tor->secondsSeeding;
1414     s->secondsDownloading = tor->secondsDownloading;
1415     s->idleSecs = torrentGetIdleSecs(tor);
1416 
1417     s->corruptEver = tor->corruptCur + tor->corruptPrev;
1418     s->downloadedEver = tor->downloadedCur + tor->downloadedPrev;
1419     s->uploadedEver = tor->uploadedCur + tor->uploadedPrev;
1420     s->haveValid = tr_cpHaveValid(&tor->completion);
1421     s->haveUnchecked = tr_torrentHaveTotal(tor) - s->haveValid;
1422     s->desiredAvailable = tr_peerMgrGetDesiredAvailable(tor);
1423 
1424     s->ratio = tr_getRatio(s->uploadedEver, s->downloadedEver != 0 ? s->downloadedEver : s->haveValid);
1425 
1426     seedRatioApplies = tr_torrentGetSeedRatioBytes(tor, &seedRatioBytesLeft, &seedRatioBytesGoal);
1427 
1428     switch (s->activity)
1429     {
1430     /* etaXLSpeed exists because if we use the piece speed directly,
1431      * brief fluctuations cause the ETA to jump all over the place.
1432      * so, etaXLSpeed is a smoothed-out version of the piece speed
1433      * to dampen the effect of fluctuations */
1434     case TR_STATUS_DOWNLOAD:
1435         if (tor->etaDLSpeedCalculatedAt + 800 < now)
1436         {
1437             tor->etaDLSpeed_Bps = tor->etaDLSpeedCalculatedAt + 4000 < now ?
1438                 pieceDownloadSpeed_Bps : /* if no recent previous speed, no need to smooth */
1439                 (tor->etaDLSpeed_Bps * 4.0 + pieceDownloadSpeed_Bps) / 5.0; /* smooth across 5 readings */
1440             tor->etaDLSpeedCalculatedAt = now;
1441         }
1442 
1443         if (s->leftUntilDone > s->desiredAvailable && tor->info.webseedCount < 1)
1444         {
1445             s->eta = TR_ETA_NOT_AVAIL;
1446         }
1447         else if (tor->etaDLSpeed_Bps == 0)
1448         {
1449             s->eta = TR_ETA_UNKNOWN;
1450         }
1451         else
1452         {
1453             s->eta = s->leftUntilDone / tor->etaDLSpeed_Bps;
1454         }
1455 
1456         s->etaIdle = TR_ETA_NOT_AVAIL;
1457         break;
1458 
1459     case TR_STATUS_SEED:
1460         if (!seedRatioApplies)
1461         {
1462             s->eta = TR_ETA_NOT_AVAIL;
1463         }
1464         else
1465         {
1466             if (tor->etaULSpeedCalculatedAt + 800 < now)
1467             {
1468                 tor->etaULSpeed_Bps = tor->etaULSpeedCalculatedAt + 4000 < now ?
1469                     pieceUploadSpeed_Bps : /* if no recent previous speed, no need to smooth */
1470                     (tor->etaULSpeed_Bps * 4.0 + pieceUploadSpeed_Bps) / 5.0; /* smooth across 5 readings */
1471                 tor->etaULSpeedCalculatedAt = now;
1472             }
1473 
1474             if (tor->etaULSpeed_Bps == 0)
1475             {
1476                 s->eta = TR_ETA_UNKNOWN;
1477             }
1478             else
1479             {
1480                 s->eta = seedRatioBytesLeft / tor->etaULSpeed_Bps;
1481             }
1482         }
1483 
1484         if (tor->etaULSpeed_Bps < 1 && tr_torrentGetSeedIdle(tor, &seedIdleMinutes))
1485         {
1486             s->etaIdle = seedIdleMinutes * 60 - s->idleSecs;
1487         }
1488         else
1489         {
1490             s->etaIdle = TR_ETA_NOT_AVAIL;
1491         }
1492 
1493         break;
1494 
1495     default:
1496         s->eta = TR_ETA_NOT_AVAIL;
1497         s->etaIdle = TR_ETA_NOT_AVAIL;
1498         break;
1499     }
1500 
1501     /* s->haveValid is here to make sure a torrent isn't marked 'finished'
1502      * when the user hits "uncheck all" prior to starting the torrent... */
1503     s->finished = tor->finishedSeedingByIdle || (seedRatioApplies && seedRatioBytesLeft == 0 && s->haveValid != 0);
1504 
1505     if (!seedRatioApplies || s->finished)
1506     {
1507         s->seedRatioPercentDone = 1;
1508     }
1509     else if (seedRatioBytesGoal == 0) /* impossible? safeguard for div by zero */
1510     {
1511         s->seedRatioPercentDone = 0;
1512     }
1513     else
1514     {
1515         s->seedRatioPercentDone = (double)(seedRatioBytesGoal - seedRatioBytesLeft) / seedRatioBytesGoal;
1516     }
1517 
1518     /* test some of the constraints */
1519     TR_ASSERT(s->sizeWhenDone <= tor->info.totalSize);
1520     TR_ASSERT(s->leftUntilDone <= s->sizeWhenDone);
1521     TR_ASSERT(s->desiredAvailable <= s->leftUntilDone);
1522 
1523     return s;
1524 }
1525 
1526 /***
1527 ****
1528 ***/
1529 
countFileBytesCompleted(tr_torrent const * tor,tr_file_index_t index)1530 static uint64_t countFileBytesCompleted(tr_torrent const* tor, tr_file_index_t index)
1531 {
1532     uint64_t total = 0;
1533     tr_file const* f = &tor->info.files[index];
1534 
1535     if (f->length != 0)
1536     {
1537         tr_block_index_t first;
1538         tr_block_index_t last;
1539         tr_torGetFileBlockRange(tor, index, &first, &last);
1540 
1541         if (first == last)
1542         {
1543             if (tr_torrentBlockIsComplete(tor, first))
1544             {
1545                 total = f->length;
1546             }
1547         }
1548         else
1549         {
1550             /* the first block */
1551             if (tr_torrentBlockIsComplete(tor, first))
1552             {
1553                 total += tor->blockSize - f->offset % tor->blockSize;
1554             }
1555 
1556             /* the middle blocks */
1557             if (first + 1 < last)
1558             {
1559                 uint64_t u = tr_bitfieldCountRange(&tor->completion.blockBitfield, first + 1, last);
1560                 u *= tor->blockSize;
1561                 total += u;
1562             }
1563 
1564             /* the last block */
1565             if (tr_torrentBlockIsComplete(tor, last))
1566             {
1567                 total += f->offset + f->length - (uint64_t)tor->blockSize * last;
1568             }
1569         }
1570     }
1571 
1572     return total;
1573 }
1574 
tr_torrentFiles(tr_torrent const * tor,tr_file_index_t * fileCount)1575 tr_file_stat* tr_torrentFiles(tr_torrent const* tor, tr_file_index_t* fileCount)
1576 {
1577     TR_ASSERT(tr_isTorrent(tor));
1578 
1579     tr_file_index_t const n = tor->info.fileCount;
1580     tr_file_stat* files = tr_new0(tr_file_stat, n);
1581     tr_file_stat* walk = files;
1582     bool const isSeed = tor->completeness == TR_SEED;
1583 
1584     for (tr_file_index_t i = 0; i < n; ++i, ++walk)
1585     {
1586         uint64_t const b = isSeed ? tor->info.files[i].length : countFileBytesCompleted(tor, i);
1587         walk->bytesCompleted = b;
1588         walk->progress = tor->info.files[i].length > 0 ? (float)b / tor->info.files[i].length : 1.0F;
1589     }
1590 
1591     if (fileCount != NULL)
1592     {
1593         *fileCount = n;
1594     }
1595 
1596     return files;
1597 }
1598 
tr_torrentFilesFree(tr_file_stat * files,tr_file_index_t fileCount UNUSED)1599 void tr_torrentFilesFree(tr_file_stat* files, tr_file_index_t fileCount UNUSED)
1600 {
1601     tr_free(files);
1602 }
1603 
1604 /***
1605 ****
1606 ***/
1607 
tr_torrentWebSpeeds_KBps(tr_torrent const * tor)1608 double* tr_torrentWebSpeeds_KBps(tr_torrent const* tor)
1609 {
1610     TR_ASSERT(tr_isTorrent(tor));
1611 
1612     return tr_peerMgrWebSpeeds_KBps(tor);
1613 }
1614 
tr_torrentPeers(tr_torrent const * tor,int * peerCount)1615 tr_peer_stat* tr_torrentPeers(tr_torrent const* tor, int* peerCount)
1616 {
1617     TR_ASSERT(tr_isTorrent(tor));
1618 
1619     return tr_peerMgrPeerStats(tor, peerCount);
1620 }
1621 
tr_torrentPeersFree(tr_peer_stat * peers,int peerCount UNUSED)1622 void tr_torrentPeersFree(tr_peer_stat* peers, int peerCount UNUSED)
1623 {
1624     tr_free(peers);
1625 }
1626 
tr_torrentTrackers(tr_torrent const * tor,int * setmeTrackerCount)1627 tr_tracker_stat* tr_torrentTrackers(tr_torrent const* tor, int* setmeTrackerCount)
1628 {
1629     TR_ASSERT(tr_isTorrent(tor));
1630 
1631     return tr_announcerStats(tor, setmeTrackerCount);
1632 }
1633 
tr_torrentTrackersFree(tr_tracker_stat * trackers,int trackerCount)1634 void tr_torrentTrackersFree(tr_tracker_stat* trackers, int trackerCount)
1635 {
1636     tr_announcerStatsFree(trackers, trackerCount);
1637 }
1638 
tr_torrentAvailability(tr_torrent const * tor,int8_t * tab,int size)1639 void tr_torrentAvailability(tr_torrent const* tor, int8_t* tab, int size)
1640 {
1641     TR_ASSERT(tr_isTorrent(tor));
1642 
1643     if (tab != NULL && size > 0)
1644     {
1645         tr_peerMgrTorrentAvailability(tor, tab, size);
1646     }
1647 }
1648 
tr_torrentAmountFinished(tr_torrent const * tor,float * tab,int size)1649 void tr_torrentAmountFinished(tr_torrent const* tor, float* tab, int size)
1650 {
1651     tr_cpGetAmountDone(&tor->completion, tab, size);
1652 }
1653 
tr_torrentResetTransferStats(tr_torrent * tor)1654 static void tr_torrentResetTransferStats(tr_torrent* tor)
1655 {
1656     tr_torrentLock(tor);
1657 
1658     tor->downloadedPrev += tor->downloadedCur;
1659     tor->downloadedCur = 0;
1660     tor->uploadedPrev += tor->uploadedCur;
1661     tor->uploadedCur = 0;
1662     tor->corruptPrev += tor->corruptCur;
1663     tor->corruptCur = 0;
1664 
1665     tr_torrentSetDirty(tor);
1666 
1667     tr_torrentUnlock(tor);
1668 }
1669 
tr_torrentSetHasPiece(tr_torrent * tor,tr_piece_index_t pieceIndex,bool has)1670 void tr_torrentSetHasPiece(tr_torrent* tor, tr_piece_index_t pieceIndex, bool has)
1671 {
1672     TR_ASSERT(tr_isTorrent(tor));
1673     TR_ASSERT(pieceIndex < tor->info.pieceCount);
1674 
1675     if (has)
1676     {
1677         tr_cpPieceAdd(&tor->completion, pieceIndex);
1678     }
1679     else
1680     {
1681         tr_cpPieceRem(&tor->completion, pieceIndex);
1682     }
1683 }
1684 
1685 /***
1686 ****
1687 ***/
1688 
1689 #ifdef TR_ENABLE_ASSERTS
1690 static bool queueIsSequenced(tr_session*);
1691 #endif
1692 
freeTorrent(tr_torrent * tor)1693 static void freeTorrent(tr_torrent* tor)
1694 {
1695     TR_ASSERT(!tor->isRunning);
1696 
1697     tr_session* session = tor->session;
1698     tr_info* inf = &tor->info;
1699     time_t const now = tr_time();
1700 
1701     tr_sessionLock(session);
1702 
1703     tr_peerMgrRemoveTorrent(tor);
1704 
1705     tr_announcerRemoveTorrent(session->announcer, tor);
1706 
1707     tr_cpDestruct(&tor->completion);
1708 
1709     tr_free(tor->downloadDir);
1710     tr_free(tor->incompleteDir);
1711 
1712     if (tor == session->torrentList)
1713     {
1714         session->torrentList = tor->next;
1715     }
1716     else
1717     {
1718         for (tr_torrent* t = session->torrentList; t != NULL; t = t->next)
1719         {
1720             if (t->next == tor)
1721             {
1722                 t->next = tor->next;
1723                 break;
1724             }
1725         }
1726     }
1727 
1728     /* decrement the torrent count */
1729     TR_ASSERT(session->torrentCount >= 1);
1730     session->torrentCount--;
1731 
1732     /* resequence the queue positions */
1733     tr_torrent* t = NULL;
1734 
1735     while ((t = tr_torrentNext(session, t)) != NULL)
1736     {
1737         if (t->queuePosition > tor->queuePosition)
1738         {
1739             t->queuePosition--;
1740             t->anyDate = now;
1741         }
1742     }
1743 
1744     TR_ASSERT(queueIsSequenced(session));
1745 
1746     tr_bandwidthDestruct(&tor->bandwidth);
1747     tr_ptrArrayDestruct(&tor->labels, tr_free);
1748 
1749     tr_metainfoFree(inf);
1750     memset(tor, ~0, sizeof(tr_torrent));
1751     tr_free(tor);
1752 
1753     tr_sessionUnlock(session);
1754 }
1755 
1756 /**
1757 ***  Start/Stop Callback
1758 **/
1759 
1760 static void torrentSetQueued(tr_torrent* tor, bool queued);
1761 
torrentStartImpl(void * vtor)1762 static void torrentStartImpl(void* vtor)
1763 {
1764     tr_torrent* tor = vtor;
1765 
1766     TR_ASSERT(tr_isTorrent(tor));
1767 
1768     tr_sessionLock(tor->session);
1769 
1770     tr_torrentRecheckCompleteness(tor);
1771     torrentSetQueued(tor, false);
1772 
1773     time_t const now = tr_time();
1774 
1775     tor->isRunning = true;
1776     tor->completeness = tr_cpGetStatus(&tor->completion);
1777     tor->startDate = now;
1778     tor->anyDate = now;
1779     tr_torrentClearError(tor);
1780     tor->finishedSeedingByIdle = false;
1781 
1782     tr_torrentResetTransferStats(tor);
1783     tr_announcerTorrentStarted(tor);
1784     tor->dhtAnnounceAt = now + tr_rand_int_weak(20);
1785     tor->dhtAnnounce6At = now + tr_rand_int_weak(20);
1786     tor->lpdAnnounceAt = now;
1787     tr_peerMgrStartTorrent(tor);
1788 
1789     tr_sessionUnlock(tor->session);
1790 }
1791 
tr_torrentGetCurrentSizeOnDisk(tr_torrent const * tor)1792 uint64_t tr_torrentGetCurrentSizeOnDisk(tr_torrent const* tor)
1793 {
1794     uint64_t byte_count = 0;
1795     tr_file_index_t const n = tor->info.fileCount;
1796 
1797     for (tr_file_index_t i = 0; i < n; ++i)
1798     {
1799         tr_sys_path_info info;
1800         char* filename = tr_torrentFindFile(tor, i);
1801 
1802         if (filename != NULL && tr_sys_path_get_info(filename, 0, &info, NULL))
1803         {
1804             byte_count += info.size;
1805         }
1806 
1807         tr_free(filename);
1808     }
1809 
1810     return byte_count;
1811 }
1812 
torrentShouldQueue(tr_torrent const * tor)1813 static bool torrentShouldQueue(tr_torrent const* tor)
1814 {
1815     tr_direction const dir = tr_torrentGetQueueDirection(tor);
1816 
1817     return tr_sessionCountQueueFreeSlots(tor->session, dir) == 0;
1818 }
1819 
torrentStart(tr_torrent * tor,bool bypass_queue)1820 static void torrentStart(tr_torrent* tor, bool bypass_queue)
1821 {
1822     switch (tr_torrentGetActivity(tor))
1823     {
1824     case TR_STATUS_SEED:
1825     case TR_STATUS_DOWNLOAD:
1826         return; /* already started */
1827         break;
1828 
1829     case TR_STATUS_SEED_WAIT:
1830     case TR_STATUS_DOWNLOAD_WAIT:
1831         if (!bypass_queue)
1832         {
1833             return; /* already queued */
1834         }
1835 
1836         break;
1837 
1838     case TR_STATUS_CHECK:
1839     case TR_STATUS_CHECK_WAIT:
1840         /* verifying right now... wait until that's done so
1841          * we'll know what completeness to use/announce */
1842         tor->startAfterVerify = true;
1843         return;
1844         break;
1845 
1846     case TR_STATUS_STOPPED:
1847         if (!bypass_queue && torrentShouldQueue(tor))
1848         {
1849             torrentSetQueued(tor, true);
1850             return;
1851         }
1852 
1853         break;
1854     }
1855 
1856     /* don't allow the torrent to be started if the files disappeared */
1857     if (setLocalErrorIfFilesDisappeared(tor))
1858     {
1859         return;
1860     }
1861 
1862     /* otherwise, start it now... */
1863     tr_sessionLock(tor->session);
1864 
1865     /* allow finished torrents to be resumed */
1866     if (tr_torrentIsSeedRatioDone(tor))
1867     {
1868         tr_logAddTorInfo(tor, "%s", _("Restarted manually -- disabling its seed ratio"));
1869         tr_torrentSetRatioMode(tor, TR_RATIOLIMIT_UNLIMITED);
1870     }
1871 
1872     /* corresponds to the peer_id sent as a tracker request parameter.
1873      * one tracker admin says: "When the same torrent is opened and
1874      * closed and opened again without quitting Transmission ...
1875      * change the peerid. It would help sometimes if a stopped event
1876      * was missed to ensure that we didn't think someone was cheating. */
1877     tr_torrentUnsetPeerId(tor);
1878     tor->isRunning = true;
1879     tr_torrentSetDirty(tor);
1880     tr_runInEventThread(tor->session, torrentStartImpl, tor);
1881 
1882     tr_sessionUnlock(tor->session);
1883 }
1884 
tr_torrentStart(tr_torrent * tor)1885 void tr_torrentStart(tr_torrent* tor)
1886 {
1887     if (tr_isTorrent(tor))
1888     {
1889         torrentStart(tor, false);
1890     }
1891 }
1892 
tr_torrentStartNow(tr_torrent * tor)1893 void tr_torrentStartNow(tr_torrent* tor)
1894 {
1895     if (tr_isTorrent(tor))
1896     {
1897         torrentStart(tor, true);
1898     }
1899 }
1900 
1901 struct verify_data
1902 {
1903     bool aborted;
1904     tr_torrent* tor;
1905     tr_verify_done_func callback_func;
1906     void* callback_data;
1907 };
1908 
onVerifyDoneThreadFunc(void * vdata)1909 static void onVerifyDoneThreadFunc(void* vdata)
1910 {
1911     struct verify_data* data = vdata;
1912     tr_torrent* tor = data->tor;
1913 
1914     if (tor->isDeleting)
1915     {
1916         goto cleanup;
1917     }
1918 
1919     if (!data->aborted)
1920     {
1921         tr_torrentRecheckCompleteness(tor);
1922     }
1923 
1924     if (data->callback_func != NULL)
1925     {
1926         (*data->callback_func)(tor, data->aborted, data->callback_data);
1927     }
1928 
1929     if (!data->aborted && tor->startAfterVerify)
1930     {
1931         tor->startAfterVerify = false;
1932         torrentStart(tor, false);
1933     }
1934 
1935 cleanup:
1936     tr_free(data);
1937 }
1938 
onVerifyDone(tr_torrent * tor,bool aborted,void * vdata)1939 static void onVerifyDone(tr_torrent* tor, bool aborted, void* vdata)
1940 {
1941     struct verify_data* data = vdata;
1942 
1943     TR_ASSERT(data->tor == tor);
1944 
1945     if (tor->isDeleting)
1946     {
1947         tr_free(data);
1948         return;
1949     }
1950 
1951     data->aborted = aborted;
1952     tr_runInEventThread(tor->session, onVerifyDoneThreadFunc, data);
1953 }
1954 
verifyTorrent(void * vdata)1955 static void verifyTorrent(void* vdata)
1956 {
1957     bool startAfter;
1958     struct verify_data* data = vdata;
1959     tr_torrent* tor = data->tor;
1960     tr_sessionLock(tor->session);
1961 
1962     if (tor->isDeleting)
1963     {
1964         tr_free(data);
1965         goto unlock;
1966     }
1967 
1968     /* if the torrent's already being verified, stop it */
1969     tr_verifyRemove(tor);
1970 
1971     startAfter = (tor->isRunning || tor->startAfterVerify) && !tor->isStopping;
1972 
1973     if (tor->isRunning)
1974     {
1975         tr_torrentStop(tor);
1976     }
1977 
1978     tor->startAfterVerify = startAfter;
1979 
1980     if (setLocalErrorIfFilesDisappeared(tor))
1981     {
1982         tor->startAfterVerify = false;
1983     }
1984     else
1985     {
1986         tr_verifyAdd(tor, onVerifyDone, data);
1987     }
1988 
1989 unlock:
1990     tr_sessionUnlock(tor->session);
1991 }
1992 
tr_torrentVerify(tr_torrent * tor,tr_verify_done_func callback_func,void * callback_data)1993 void tr_torrentVerify(tr_torrent* tor, tr_verify_done_func callback_func, void* callback_data)
1994 {
1995     struct verify_data* data;
1996 
1997     data = tr_new(struct verify_data, 1);
1998     data->tor = tor;
1999     data->aborted = false;
2000     data->callback_func = callback_func;
2001     data->callback_data = callback_data;
2002     tr_runInEventThread(tor->session, verifyTorrent, data);
2003 }
2004 
tr_torrentSave(tr_torrent * tor)2005 void tr_torrentSave(tr_torrent* tor)
2006 {
2007     TR_ASSERT(tr_isTorrent(tor));
2008 
2009     if (tor->isDirty)
2010     {
2011         tor->isDirty = false;
2012         tr_torrentSaveResume(tor);
2013     }
2014 }
2015 
stopTorrent(void * vtor)2016 static void stopTorrent(void* vtor)
2017 {
2018     tr_torrent* tor = vtor;
2019 
2020     TR_ASSERT(tr_isTorrent(tor));
2021 
2022     tr_logAddTorInfo(tor, "%s", "Pausing");
2023 
2024     tr_torrentLock(tor);
2025 
2026     tr_verifyRemove(tor);
2027     tr_peerMgrStopTorrent(tor);
2028     tr_announcerTorrentStopped(tor);
2029     tr_cacheFlushTorrent(tor->session->cache, tor);
2030 
2031     tr_fdTorrentClose(tor->session, tor->uniqueId);
2032 
2033     if (!tor->isDeleting)
2034     {
2035         tr_torrentSave(tor);
2036     }
2037 
2038     torrentSetQueued(tor, false);
2039 
2040     tr_torrentUnlock(tor);
2041 
2042     if (tor->magnetVerify)
2043     {
2044         tor->magnetVerify = false;
2045         tr_logAddTorInfo(tor, "%s", "Magnet Verify");
2046         refreshCurrentDir(tor);
2047         tr_torrentVerify(tor, NULL, NULL);
2048     }
2049 }
2050 
tr_torrentStop(tr_torrent * tor)2051 void tr_torrentStop(tr_torrent* tor)
2052 {
2053     TR_ASSERT(tr_isTorrent(tor));
2054 
2055     if (tr_isTorrent(tor))
2056     {
2057         tr_sessionLock(tor->session);
2058 
2059         tor->isRunning = false;
2060         tor->isStopping = false;
2061         tr_torrentSetDirty(tor);
2062         tr_runInEventThread(tor->session, stopTorrent, tor);
2063 
2064         tr_sessionUnlock(tor->session);
2065     }
2066 }
2067 
closeTorrent(void * vtor)2068 static void closeTorrent(void* vtor)
2069 {
2070     tr_torrent* tor = vtor;
2071 
2072     TR_ASSERT(tr_isTorrent(tor));
2073 
2074     tr_variant* d = tr_variantListAddDict(&tor->session->removedTorrents, 2);
2075     tr_variantDictAddInt(d, TR_KEY_id, tor->uniqueId);
2076     tr_variantDictAddInt(d, TR_KEY_date, tr_time());
2077 
2078     tr_logAddTorInfo(tor, "%s", _("Removing torrent"));
2079 
2080     tor->magnetVerify = false;
2081     stopTorrent(tor);
2082 
2083     if (tor->isDeleting)
2084     {
2085         tr_metainfoRemoveSaved(tor->session, &tor->info);
2086         tr_torrentRemoveResume(tor);
2087     }
2088 
2089     tor->isRunning = false;
2090     freeTorrent(tor);
2091 }
2092 
tr_torrentFree(tr_torrent * tor)2093 void tr_torrentFree(tr_torrent* tor)
2094 {
2095     if (tr_isTorrent(tor))
2096     {
2097         tr_session* session = tor->session;
2098 
2099         TR_ASSERT(tr_isSession(session));
2100 
2101         tr_sessionLock(session);
2102 
2103         tr_torrentClearCompletenessCallback(tor);
2104         tr_runInEventThread(session, closeTorrent, tor);
2105 
2106         tr_sessionUnlock(session);
2107     }
2108 }
2109 
2110 struct remove_data
2111 {
2112     tr_torrent* tor;
2113     bool deleteFlag;
2114     tr_fileFunc deleteFunc;
2115 };
2116 
2117 static void tr_torrentDeleteLocalData(tr_torrent*, tr_fileFunc);
2118 
removeTorrent(void * vdata)2119 static void removeTorrent(void* vdata)
2120 {
2121     struct remove_data* data = vdata;
2122     tr_session* session = data->tor->session;
2123     tr_sessionLock(session);
2124 
2125     if (data->deleteFlag)
2126     {
2127         tr_torrentDeleteLocalData(data->tor, data->deleteFunc);
2128     }
2129 
2130     tr_torrentClearCompletenessCallback(data->tor);
2131     closeTorrent(data->tor);
2132     tr_free(data);
2133 
2134     tr_sessionUnlock(session);
2135 }
2136 
tr_torrentRemove(tr_torrent * tor,bool deleteFlag,tr_fileFunc deleteFunc)2137 void tr_torrentRemove(tr_torrent* tor, bool deleteFlag, tr_fileFunc deleteFunc)
2138 {
2139     TR_ASSERT(tr_isTorrent(tor));
2140 
2141     tor->isDeleting = true;
2142 
2143     struct remove_data* data = tr_new0(struct remove_data, 1);
2144     data->tor = tor;
2145     data->deleteFlag = deleteFlag;
2146     data->deleteFunc = deleteFunc;
2147     tr_runInEventThread(tor->session, removeTorrent, data);
2148 }
2149 
2150 /**
2151 ***  Completeness
2152 **/
2153 
getCompletionString(int type)2154 static char const* getCompletionString(int type)
2155 {
2156     switch (type)
2157     {
2158     case TR_PARTIAL_SEED:
2159         /* Translators: this is a minor point that's safe to skip over, but FYI:
2160            "Complete" and "Done" are specific, different terms in Transmission:
2161            "Complete" means we've downloaded every file in the torrent.
2162            "Done" means we're done downloading the files we wanted, but NOT all
2163            that exist */
2164         return _("Done");
2165 
2166     case TR_SEED:
2167         return _("Complete");
2168 
2169     default:
2170         return _("Incomplete");
2171     }
2172 }
2173 
fireCompletenessChange(tr_torrent * tor,tr_completeness status,bool wasRunning)2174 static void fireCompletenessChange(tr_torrent* tor, tr_completeness status, bool wasRunning)
2175 {
2176     TR_ASSERT(status == TR_LEECH || status == TR_SEED || status == TR_PARTIAL_SEED);
2177 
2178     if (tor->completeness_func != NULL)
2179     {
2180         (*tor->completeness_func)(tor, status, wasRunning, tor->completeness_func_user_data);
2181     }
2182 }
2183 
tr_torrentSetCompletenessCallback(tr_torrent * tor,tr_torrent_completeness_func func,void * user_data)2184 void tr_torrentSetCompletenessCallback(tr_torrent* tor, tr_torrent_completeness_func func, void* user_data)
2185 {
2186     TR_ASSERT(tr_isTorrent(tor));
2187 
2188     tor->completeness_func = func;
2189     tor->completeness_func_user_data = user_data;
2190 }
2191 
tr_torrentClearCompletenessCallback(tr_torrent * torrent)2192 void tr_torrentClearCompletenessCallback(tr_torrent* torrent)
2193 {
2194     tr_torrentSetCompletenessCallback(torrent, NULL, NULL);
2195 }
2196 
tr_torrentSetRatioLimitHitCallback(tr_torrent * tor,tr_torrent_ratio_limit_hit_func func,void * user_data)2197 void tr_torrentSetRatioLimitHitCallback(tr_torrent* tor, tr_torrent_ratio_limit_hit_func func, void* user_data)
2198 {
2199     TR_ASSERT(tr_isTorrent(tor));
2200 
2201     tor->ratio_limit_hit_func = func;
2202     tor->ratio_limit_hit_func_user_data = user_data;
2203 }
2204 
tr_torrentClearRatioLimitHitCallback(tr_torrent * torrent)2205 void tr_torrentClearRatioLimitHitCallback(tr_torrent* torrent)
2206 {
2207     tr_torrentSetRatioLimitHitCallback(torrent, NULL, NULL);
2208 }
2209 
tr_torrentSetIdleLimitHitCallback(tr_torrent * tor,tr_torrent_idle_limit_hit_func func,void * user_data)2210 void tr_torrentSetIdleLimitHitCallback(tr_torrent* tor, tr_torrent_idle_limit_hit_func func, void* user_data)
2211 {
2212     TR_ASSERT(tr_isTorrent(tor));
2213 
2214     tor->idle_limit_hit_func = func;
2215     tor->idle_limit_hit_func_user_data = user_data;
2216 }
2217 
tr_torrentClearIdleLimitHitCallback(tr_torrent * torrent)2218 void tr_torrentClearIdleLimitHitCallback(tr_torrent* torrent)
2219 {
2220     tr_torrentSetIdleLimitHitCallback(torrent, NULL, NULL);
2221 }
2222 
get_local_time_str(char * const buffer,size_t const buffer_len)2223 static void get_local_time_str(char* const buffer, size_t const buffer_len)
2224 {
2225     time_t const now = tr_time();
2226 
2227     tr_strlcpy(buffer, ctime(&now), buffer_len);
2228 
2229     char* newline_pos = strchr(buffer, '\n');
2230 
2231     /* ctime() includes '\n', but it's better to be safe */
2232     if (newline_pos != NULL)
2233     {
2234         *newline_pos = '\0';
2235     }
2236 }
2237 
torrentCallScript(tr_torrent const * tor,char const * script)2238 static void torrentCallScript(tr_torrent const* tor, char const* script)
2239 {
2240     if (tr_str_is_empty(script))
2241     {
2242         return;
2243     }
2244 
2245     char time_str[32];
2246     get_local_time_str(time_str, TR_N_ELEMENTS(time_str));
2247 
2248     char* const torrent_dir = tr_sys_path_native_separators(tr_strdup(tor->currentDir));
2249 
2250     char* const cmd[] =
2251     {
2252         tr_strdup(script),
2253         NULL
2254     };
2255 
2256     char* labels = tr_strjoin((char const* const*)tr_ptrArrayBase(&tor->labels), tr_ptrArraySize(&tor->labels), ",");
2257 
2258     char* const env[] =
2259     {
2260         tr_strdup_printf("TR_APP_VERSION=%s", SHORT_VERSION_STRING),
2261         tr_strdup_printf("TR_TIME_LOCALTIME=%s", time_str),
2262         tr_strdup_printf("TR_TORRENT_DIR=%s", torrent_dir),
2263         tr_strdup_printf("TR_TORRENT_HASH=%s", tor->info.hashString),
2264         tr_strdup_printf("TR_TORRENT_ID=%d", tr_torrentId(tor)),
2265         tr_strdup_printf("TR_TORRENT_NAME=%s", tr_torrentName(tor)),
2266         tr_strdup_printf("TR_TORRENT_LABELS=%s", labels),
2267         NULL
2268     };
2269 
2270     tr_logAddTorInfo(tor, "Calling script \"%s\"", script);
2271 
2272     tr_error* error = NULL;
2273 
2274     if (!tr_spawn_async(cmd, env, TR_IF_WIN32("\\", "/"), &error))
2275     {
2276         tr_logAddTorErr(tor, "Error executing script \"%s\" (%d): %s", script, error->code, error->message);
2277         tr_error_free(error);
2278     }
2279 
2280     tr_free_ptrv((void* const*)env);
2281     tr_free_ptrv((void* const*)cmd);
2282     tr_free(labels);
2283     tr_free(torrent_dir);
2284 }
2285 
tr_torrentRecheckCompleteness(tr_torrent * tor)2286 void tr_torrentRecheckCompleteness(tr_torrent* tor)
2287 {
2288     tr_completeness completeness;
2289 
2290     tr_torrentLock(tor);
2291 
2292     completeness = tr_cpGetStatus(&tor->completion);
2293 
2294     if (completeness != tor->completeness)
2295     {
2296         bool const recentChange = tor->downloadedCur != 0;
2297         bool const wasLeeching = !tr_torrentIsSeed(tor);
2298         bool const wasRunning = tor->isRunning;
2299 
2300         if (recentChange)
2301         {
2302             tr_logAddTorInfo(tor, _("State changed from \"%1$s\" to \"%2$s\""), getCompletionString(tor->completeness),
2303                 getCompletionString(completeness));
2304         }
2305 
2306         tor->completeness = completeness;
2307         tr_fdTorrentClose(tor->session, tor->uniqueId);
2308 
2309         if (tr_torrentIsSeed(tor))
2310         {
2311             if (recentChange)
2312             {
2313                 tr_announcerTorrentCompleted(tor);
2314                 tor->doneDate = tor->anyDate = tr_time();
2315             }
2316 
2317             if (wasLeeching && wasRunning)
2318             {
2319                 /* clear interested flag on all peers */
2320                 tr_peerMgrClearInterest(tor);
2321             }
2322 
2323             if (tor->currentDir == tor->incompleteDir)
2324             {
2325                 tr_torrentSetLocation(tor, tor->downloadDir, true, NULL, NULL);
2326             }
2327         }
2328 
2329         fireCompletenessChange(tor, completeness, wasRunning);
2330 
2331         if (tr_torrentIsSeed(tor) && wasLeeching && wasRunning)
2332         {
2333             /* if completeness was TR_LEECH, the seed limit check
2334                will have been skipped in bandwidthPulse */
2335             tr_torrentCheckSeedLimit(tor);
2336         }
2337 
2338         tr_torrentSetDirty(tor);
2339 
2340         if (tr_torrentIsSeed(tor) && tr_sessionIsTorrentDoneScriptEnabled(tor->session))
2341         {
2342             tr_torrentSave(tor);
2343 
2344             torrentCallScript(tor, tr_sessionGetTorrentDoneScript(tor->session));
2345         }
2346     }
2347 
2348     tr_torrentUnlock(tor);
2349 }
2350 
2351 /***
2352 ****
2353 ***/
2354 
tr_torrentFireMetadataCompleted(tr_torrent * tor)2355 static void tr_torrentFireMetadataCompleted(tr_torrent* tor)
2356 {
2357     TR_ASSERT(tr_isTorrent(tor));
2358 
2359     if (tor->metadata_func != NULL)
2360     {
2361         (*tor->metadata_func)(tor, tor->metadata_func_user_data);
2362     }
2363 }
2364 
tr_torrentSetMetadataCallback(tr_torrent * tor,tr_torrent_metadata_func func,void * user_data)2365 void tr_torrentSetMetadataCallback(tr_torrent* tor, tr_torrent_metadata_func func, void* user_data)
2366 {
2367     TR_ASSERT(tr_isTorrent(tor));
2368 
2369     tor->metadata_func = func;
2370     tor->metadata_func_user_data = user_data;
2371 }
2372 
2373 /**
2374 ***  File priorities
2375 **/
2376 
tr_torrentInitFilePriority(tr_torrent * tor,tr_file_index_t fileIndex,tr_priority_t priority)2377 void tr_torrentInitFilePriority(tr_torrent* tor, tr_file_index_t fileIndex, tr_priority_t priority)
2378 {
2379     TR_ASSERT(tr_isTorrent(tor));
2380     TR_ASSERT(fileIndex < tor->info.fileCount);
2381     TR_ASSERT(tr_isPriority(priority));
2382 
2383     tr_file* file = &tor->info.files[fileIndex];
2384 
2385     file->priority = priority;
2386 
2387     for (tr_piece_index_t i = file->firstPiece; i <= file->lastPiece; ++i)
2388     {
2389         tor->info.pieces[i].priority = calculatePiecePriority(tor, i, fileIndex);
2390     }
2391 }
2392 
tr_torrentSetFilePriorities(tr_torrent * tor,tr_file_index_t const * files,tr_file_index_t fileCount,tr_priority_t priority)2393 void tr_torrentSetFilePriorities(tr_torrent* tor, tr_file_index_t const* files, tr_file_index_t fileCount,
2394     tr_priority_t priority)
2395 {
2396     TR_ASSERT(tr_isTorrent(tor));
2397 
2398     tr_torrentLock(tor);
2399 
2400     for (tr_file_index_t i = 0; i < fileCount; ++i)
2401     {
2402         if (files[i] < tor->info.fileCount)
2403         {
2404             tr_torrentInitFilePriority(tor, files[i], priority);
2405         }
2406     }
2407 
2408     tr_torrentSetDirty(tor);
2409     tr_peerMgrRebuildRequests(tor);
2410 
2411     tr_torrentUnlock(tor);
2412 }
2413 
tr_torrentGetFilePriorities(tr_torrent const * tor)2414 tr_priority_t* tr_torrentGetFilePriorities(tr_torrent const* tor)
2415 {
2416     TR_ASSERT(tr_isTorrent(tor));
2417 
2418     tr_priority_t* p = tr_new0(tr_priority_t, tor->info.fileCount);
2419 
2420     for (tr_file_index_t i = 0; i < tor->info.fileCount; ++i)
2421     {
2422         p[i] = tor->info.files[i].priority;
2423     }
2424 
2425     return p;
2426 }
2427 
2428 /**
2429 ***  File DND
2430 **/
2431 
setFileDND(tr_torrent * tor,tr_file_index_t fileIndex,bool doDownload)2432 static void setFileDND(tr_torrent* tor, tr_file_index_t fileIndex, bool doDownload)
2433 {
2434     bool const dnd = !doDownload;
2435     tr_piece_index_t firstPiece;
2436     bool firstPieceDND;
2437     tr_piece_index_t lastPiece;
2438     bool lastPieceDND;
2439     tr_file* file = &tor->info.files[fileIndex];
2440 
2441     file->dnd = dnd;
2442     firstPiece = file->firstPiece;
2443     lastPiece = file->lastPiece;
2444 
2445     /* can't set the first piece to DND unless
2446        every file using that piece is DND */
2447     firstPieceDND = dnd;
2448 
2449     if (fileIndex > 0)
2450     {
2451         for (tr_file_index_t i = fileIndex - 1; firstPieceDND; --i)
2452         {
2453             if (tor->info.files[i].lastPiece != firstPiece)
2454             {
2455                 break;
2456             }
2457 
2458             firstPieceDND = tor->info.files[i].dnd;
2459 
2460             if (i == 0)
2461             {
2462                 break;
2463             }
2464         }
2465     }
2466 
2467     /* can't set the last piece to DND unless
2468        every file using that piece is DND */
2469     lastPieceDND = dnd;
2470 
2471     for (tr_file_index_t i = fileIndex + 1; lastPieceDND && i < tor->info.fileCount; ++i)
2472     {
2473         if (tor->info.files[i].firstPiece != lastPiece)
2474         {
2475             break;
2476         }
2477 
2478         lastPieceDND = tor->info.files[i].dnd;
2479     }
2480 
2481     if (firstPiece == lastPiece)
2482     {
2483         tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
2484     }
2485     else
2486     {
2487         tor->info.pieces[firstPiece].dnd = firstPieceDND;
2488         tor->info.pieces[lastPiece].dnd = lastPieceDND;
2489 
2490         for (tr_piece_index_t pp = firstPiece + 1; pp < lastPiece; ++pp)
2491         {
2492             tor->info.pieces[pp].dnd = dnd;
2493         }
2494     }
2495 }
2496 
tr_torrentInitFileDLs(tr_torrent * tor,tr_file_index_t const * files,tr_file_index_t fileCount,bool doDownload)2497 void tr_torrentInitFileDLs(tr_torrent* tor, tr_file_index_t const* files, tr_file_index_t fileCount, bool doDownload)
2498 {
2499     TR_ASSERT(tr_isTorrent(tor));
2500 
2501     tr_torrentLock(tor);
2502 
2503     for (tr_file_index_t i = 0; i < fileCount; ++i)
2504     {
2505         if (files[i] < tor->info.fileCount)
2506         {
2507             setFileDND(tor, files[i], doDownload);
2508         }
2509     }
2510 
2511     tr_cpInvalidateDND(&tor->completion);
2512 
2513     tr_torrentUnlock(tor);
2514 }
2515 
tr_torrentSetFileDLs(tr_torrent * tor,tr_file_index_t const * files,tr_file_index_t fileCount,bool doDownload)2516 void tr_torrentSetFileDLs(tr_torrent* tor, tr_file_index_t const* files, tr_file_index_t fileCount, bool doDownload)
2517 {
2518     TR_ASSERT(tr_isTorrent(tor));
2519 
2520     tr_torrentLock(tor);
2521 
2522     tr_torrentInitFileDLs(tor, files, fileCount, doDownload);
2523     tr_torrentSetDirty(tor);
2524     tr_torrentRecheckCompleteness(tor);
2525     tr_peerMgrRebuildRequests(tor);
2526 
2527     tr_torrentUnlock(tor);
2528 }
2529 
2530 /***
2531 ****
2532 ***/
2533 
tr_torrentSetLabels(tr_torrent * tor,tr_ptrArray * labels)2534 void tr_torrentSetLabels(tr_torrent* tor, tr_ptrArray* labels)
2535 {
2536     TR_ASSERT(tr_isTorrent(tor));
2537 
2538     tr_torrentLock(tor);
2539 
2540     tr_ptrArrayDestruct(&tor->labels, tr_free);
2541     tor->labels = TR_PTR_ARRAY_INIT;
2542     char** l = (char**)tr_ptrArrayBase(labels);
2543     int const n = tr_ptrArraySize(labels);
2544     for (int i = 0; i < n; i++)
2545     {
2546         tr_ptrArrayAppend(&tor->labels, tr_strdup(l[i]));
2547     }
2548 
2549     tr_torrentSetDirty(tor);
2550 
2551     tr_torrentUnlock(tor);
2552 }
2553 
2554 /***
2555 ****
2556 ***/
2557 
tr_torrentGetPriority(tr_torrent const * tor)2558 tr_priority_t tr_torrentGetPriority(tr_torrent const* tor)
2559 {
2560     TR_ASSERT(tr_isTorrent(tor));
2561 
2562     return tor->bandwidth.priority;
2563 }
2564 
tr_torrentSetPriority(tr_torrent * tor,tr_priority_t priority)2565 void tr_torrentSetPriority(tr_torrent* tor, tr_priority_t priority)
2566 {
2567     TR_ASSERT(tr_isTorrent(tor));
2568     TR_ASSERT(tr_isPriority(priority));
2569 
2570     if (tor->bandwidth.priority != priority)
2571     {
2572         tor->bandwidth.priority = priority;
2573 
2574         tr_torrentSetDirty(tor);
2575     }
2576 }
2577 
2578 /***
2579 ****
2580 ***/
2581 
tr_torrentSetPeerLimit(tr_torrent * tor,uint16_t maxConnectedPeers)2582 void tr_torrentSetPeerLimit(tr_torrent* tor, uint16_t maxConnectedPeers)
2583 {
2584     TR_ASSERT(tr_isTorrent(tor));
2585 
2586     if (tor->maxConnectedPeers != maxConnectedPeers)
2587     {
2588         tor->maxConnectedPeers = maxConnectedPeers;
2589 
2590         tr_torrentSetDirty(tor);
2591     }
2592 }
2593 
tr_torrentGetPeerLimit(tr_torrent const * tor)2594 uint16_t tr_torrentGetPeerLimit(tr_torrent const* tor)
2595 {
2596     TR_ASSERT(tr_isTorrent(tor));
2597 
2598     return tor->maxConnectedPeers;
2599 }
2600 
2601 /***
2602 ****
2603 ***/
2604 
tr_torrentGetBlockLocation(tr_torrent const * tor,tr_block_index_t block,tr_piece_index_t * piece,uint32_t * offset,uint32_t * length)2605 void tr_torrentGetBlockLocation(tr_torrent const* tor, tr_block_index_t block, tr_piece_index_t* piece, uint32_t* offset,
2606     uint32_t* length)
2607 {
2608     uint64_t pos = block;
2609     pos *= tor->blockSize;
2610     *piece = pos / tor->info.pieceSize;
2611     *offset = pos - *piece * tor->info.pieceSize;
2612     *length = tr_torBlockCountBytes(tor, block);
2613 }
2614 
_tr_block(tr_torrent const * tor,tr_piece_index_t index,uint32_t offset)2615 tr_block_index_t _tr_block(tr_torrent const* tor, tr_piece_index_t index, uint32_t offset)
2616 {
2617     TR_ASSERT(tr_isTorrent(tor));
2618 
2619     tr_block_index_t ret;
2620 
2621     ret = index;
2622     ret *= tor->info.pieceSize / tor->blockSize;
2623     ret += offset / tor->blockSize;
2624     return ret;
2625 }
2626 
tr_torrentReqIsValid(tr_torrent const * tor,tr_piece_index_t index,uint32_t offset,uint32_t length)2627 bool tr_torrentReqIsValid(tr_torrent const* tor, tr_piece_index_t index, uint32_t offset, uint32_t length)
2628 {
2629     TR_ASSERT(tr_isTorrent(tor));
2630 
2631     int err = 0;
2632 
2633     if (index >= tor->info.pieceCount)
2634     {
2635         err = 1;
2636     }
2637     else if (length < 1)
2638     {
2639         err = 2;
2640     }
2641     else if (offset + length > tr_torPieceCountBytes(tor, index))
2642     {
2643         err = 3;
2644     }
2645     else if (length > MAX_BLOCK_SIZE)
2646     {
2647         err = 4;
2648     }
2649     else if (tr_pieceOffset(tor, index, offset, length) > tor->info.totalSize)
2650     {
2651         err = 5;
2652     }
2653 
2654     if (err != 0)
2655     {
2656         tr_logAddTorDbg(tor, "index %lu offset %lu length %lu err %d\n", (unsigned long)index, (unsigned long)offset,
2657             (unsigned long)length, err);
2658     }
2659 
2660     return err == 0;
2661 }
2662 
tr_pieceOffset(tr_torrent const * tor,tr_piece_index_t index,uint32_t offset,uint32_t length)2663 uint64_t tr_pieceOffset(tr_torrent const* tor, tr_piece_index_t index, uint32_t offset, uint32_t length)
2664 {
2665     TR_ASSERT(tr_isTorrent(tor));
2666 
2667     uint64_t ret;
2668 
2669     ret = tor->info.pieceSize;
2670     ret *= index;
2671     ret += offset;
2672     ret += length;
2673     return ret;
2674 }
2675 
tr_torGetFileBlockRange(tr_torrent const * tor,tr_file_index_t const file,tr_block_index_t * first,tr_block_index_t * last)2676 void tr_torGetFileBlockRange(tr_torrent const* tor, tr_file_index_t const file, tr_block_index_t* first, tr_block_index_t* last)
2677 {
2678     tr_file const* f = &tor->info.files[file];
2679     uint64_t offset = f->offset;
2680 
2681     *first = offset / tor->blockSize;
2682 
2683     if (f->length == 0)
2684     {
2685         *last = *first;
2686     }
2687     else
2688     {
2689         offset += f->length - 1;
2690         *last = offset / tor->blockSize;
2691     }
2692 }
2693 
tr_torGetPieceBlockRange(tr_torrent const * tor,tr_piece_index_t const piece,tr_block_index_t * first,tr_block_index_t * last)2694 void tr_torGetPieceBlockRange(tr_torrent const* tor, tr_piece_index_t const piece, tr_block_index_t* first,
2695     tr_block_index_t* last)
2696 {
2697     uint64_t offset = tor->info.pieceSize;
2698     offset *= piece;
2699     *first = offset / tor->blockSize;
2700     offset += tr_torPieceCountBytes(tor, piece) - 1;
2701     *last = offset / tor->blockSize;
2702 }
2703 
2704 /***
2705 ****
2706 ***/
2707 
tr_torrentSetPieceChecked(tr_torrent * tor,tr_piece_index_t pieceIndex)2708 void tr_torrentSetPieceChecked(tr_torrent* tor, tr_piece_index_t pieceIndex)
2709 {
2710     TR_ASSERT(tr_isTorrent(tor));
2711     TR_ASSERT(pieceIndex < tor->info.pieceCount);
2712 
2713     tor->info.pieces[pieceIndex].timeChecked = tr_time();
2714 }
2715 
tr_torrentSetChecked(tr_torrent * tor,time_t when)2716 void tr_torrentSetChecked(tr_torrent* tor, time_t when)
2717 {
2718     TR_ASSERT(tr_isTorrent(tor));
2719 
2720     for (tr_piece_index_t i = 0; i < tor->info.pieceCount; ++i)
2721     {
2722         tor->info.pieces[i].timeChecked = when;
2723     }
2724 }
2725 
tr_torrentCheckPiece(tr_torrent * tor,tr_piece_index_t pieceIndex)2726 bool tr_torrentCheckPiece(tr_torrent* tor, tr_piece_index_t pieceIndex)
2727 {
2728     bool const pass = tr_ioTestPiece(tor, pieceIndex);
2729 
2730     tr_deeplog_tor(tor, "[LAZY] tr_torrentCheckPiece tested piece %zu, pass==%d", (size_t)pieceIndex, (int)pass);
2731     tr_torrentSetHasPiece(tor, pieceIndex, pass);
2732     tr_torrentSetPieceChecked(tor, pieceIndex);
2733     tor->anyDate = tr_time();
2734     tr_torrentSetDirty(tor);
2735 
2736     return pass;
2737 }
2738 
tr_torrentGetFileMTime(tr_torrent const * tor,tr_file_index_t i)2739 time_t tr_torrentGetFileMTime(tr_torrent const* tor, tr_file_index_t i)
2740 {
2741     time_t mtime = 0;
2742 
2743     if (!tr_fdFileGetCachedMTime(tor->session, tor->uniqueId, i, &mtime))
2744     {
2745         tr_torrentFindFile2(tor, i, NULL, NULL, &mtime);
2746     }
2747 
2748     return mtime;
2749 }
2750 
tr_torrentPieceNeedsCheck(tr_torrent const * tor,tr_piece_index_t p)2751 bool tr_torrentPieceNeedsCheck(tr_torrent const* tor, tr_piece_index_t p)
2752 {
2753     uint64_t unused;
2754     tr_file_index_t f;
2755     tr_info const* inf = tr_torrentInfo(tor);
2756 
2757     /* if we've never checked this piece, then it needs to be checked */
2758     if (inf->pieces[p].timeChecked == 0)
2759     {
2760         return true;
2761     }
2762 
2763     /* If we think we've completed one of the files in this piece,
2764      * but it's been modified since we last checked it,
2765      * then it needs to be rechecked */
2766     tr_ioFindFileLocation(tor, p, 0, &f, &unused);
2767 
2768     for (tr_file_index_t i = f; i < inf->fileCount && pieceHasFile(p, &inf->files[i]); ++i)
2769     {
2770         if (tr_cpFileIsComplete(&tor->completion, i))
2771         {
2772             if (tr_torrentGetFileMTime(tor, i) > inf->pieces[p].timeChecked)
2773             {
2774                 return true;
2775             }
2776         }
2777     }
2778 
2779     return false;
2780 }
2781 
2782 /***
2783 ****
2784 ***/
2785 
compareTrackerByTier(void const * va,void const * vb)2786 static int compareTrackerByTier(void const* va, void const* vb)
2787 {
2788     tr_tracker_info const* a = va;
2789     tr_tracker_info const* b = vb;
2790 
2791     /* sort by tier */
2792     if (a->tier != b->tier)
2793     {
2794         return a->tier - b->tier;
2795     }
2796 
2797     /* get the effects of a stable sort by comparing the two elements' addresses */
2798     return a - b;
2799 }
2800 
tr_torrentSetAnnounceList(tr_torrent * tor,tr_tracker_info const * trackers_in,int trackerCount)2801 bool tr_torrentSetAnnounceList(tr_torrent* tor, tr_tracker_info const* trackers_in, int trackerCount)
2802 {
2803     TR_ASSERT(tr_isTorrent(tor));
2804 
2805     tr_torrentLock(tor);
2806 
2807     tr_variant metainfo;
2808     bool ok = true;
2809     tr_tracker_info* trackers;
2810 
2811     /* ensure the trackers' tiers are in ascending order */
2812     trackers = tr_memdup(trackers_in, sizeof(tr_tracker_info) * trackerCount);
2813     qsort(trackers, trackerCount, sizeof(tr_tracker_info), compareTrackerByTier);
2814 
2815     /* look for bad URLs */
2816     for (int i = 0; ok && i < trackerCount; ++i)
2817     {
2818         if (!tr_urlIsValidTracker(trackers[i].announce))
2819         {
2820             ok = false;
2821         }
2822     }
2823 
2824     /* save to the .torrent file */
2825     if (ok && tr_variantFromFile(&metainfo, TR_VARIANT_FMT_BENC, tor->info.torrent, NULL))
2826     {
2827         bool hasInfo;
2828         tr_info tmpInfo;
2829 
2830         /* remove the old fields */
2831         tr_variantDictRemove(&metainfo, TR_KEY_announce);
2832         tr_variantDictRemove(&metainfo, TR_KEY_announce_list);
2833 
2834         /* add the new fields */
2835         if (trackerCount > 0)
2836         {
2837             tr_variantDictAddStr(&metainfo, TR_KEY_announce, trackers[0].announce);
2838         }
2839 
2840         if (trackerCount > 1)
2841         {
2842             int prevTier = -1;
2843             tr_variant* tier = NULL;
2844             tr_variant* announceList = tr_variantDictAddList(&metainfo, TR_KEY_announce_list, 0);
2845 
2846             for (int i = 0; i < trackerCount; ++i)
2847             {
2848                 if (prevTier != trackers[i].tier)
2849                 {
2850                     prevTier = trackers[i].tier;
2851                     tier = tr_variantListAddList(announceList, 0);
2852                 }
2853 
2854                 tr_variantListAddStr(tier, trackers[i].announce);
2855             }
2856         }
2857 
2858         /* try to parse it back again, to make sure it's good */
2859         memset(&tmpInfo, 0, sizeof(tr_info));
2860 
2861         if (tr_metainfoParse(tor->session, &metainfo, &tmpInfo, &hasInfo, &tor->infoDictLength))
2862         {
2863             /* it's good, so keep these new trackers and free the old ones */
2864 
2865             tr_info swap;
2866             swap.trackers = tor->info.trackers;
2867             swap.trackerCount = tor->info.trackerCount;
2868             tor->info.trackers = tmpInfo.trackers;
2869             tor->info.trackerCount = tmpInfo.trackerCount;
2870             tmpInfo.trackers = swap.trackers;
2871             tmpInfo.trackerCount = swap.trackerCount;
2872             tr_torrentMarkEdited(tor);
2873 
2874             tr_metainfoFree(&tmpInfo);
2875             tr_variantToFile(&metainfo, TR_VARIANT_FMT_BENC, tor->info.torrent);
2876         }
2877 
2878         /* cleanup */
2879         tr_variantFree(&metainfo);
2880 
2881         /* if we had a tracker-related error on this torrent,
2882          * and that tracker's been removed,
2883          * then clear the error */
2884         if (tor->error == TR_STAT_TRACKER_WARNING || tor->error == TR_STAT_TRACKER_ERROR)
2885         {
2886             bool clear = true;
2887 
2888             for (int i = 0; clear && i < trackerCount; ++i)
2889             {
2890                 if (strcmp(trackers[i].announce, tor->errorTracker) == 0)
2891                 {
2892                     clear = false;
2893                 }
2894             }
2895 
2896             if (clear)
2897             {
2898                 tr_torrentClearError(tor);
2899             }
2900         }
2901 
2902         /* tell the announcer to reload this torrent's tracker list */
2903         tr_announcerResetTorrent(tor->session->announcer, tor);
2904     }
2905 
2906     tr_torrentUnlock(tor);
2907 
2908     tr_free(trackers);
2909     return ok;
2910 }
2911 
2912 /**
2913 ***
2914 **/
2915 
2916 #define BACK_COMPAT_FUNC(oldname, newname) \
2917     void oldname(tr_torrent * tor, time_t t) { newname(tor, t); }
BACK_COMPAT_FUNC(tr_torrentSetAddedDate,tr_torrentSetDateAdded)2918 BACK_COMPAT_FUNC(tr_torrentSetAddedDate, tr_torrentSetDateAdded)
2919 BACK_COMPAT_FUNC(tr_torrentSetActivityDate, tr_torrentSetDateActive)
2920 BACK_COMPAT_FUNC(tr_torrentSetDoneDate, tr_torrentSetDateDone)
2921 #undef BACK_COMPAT_FUNC
2922 
2923 void tr_torrentSetDateAdded(tr_torrent* tor, time_t t)
2924 {
2925     TR_ASSERT(tr_isTorrent(tor));
2926 
2927     tor->addedDate = t;
2928     tor->anyDate = MAX(tor->anyDate, tor->addedDate);
2929 }
2930 
tr_torrentSetDateActive(tr_torrent * tor,time_t t)2931 void tr_torrentSetDateActive(tr_torrent* tor, time_t t)
2932 {
2933     TR_ASSERT(tr_isTorrent(tor));
2934 
2935     tor->activityDate = t;
2936     tor->anyDate = MAX(tor->anyDate, tor->activityDate);
2937 }
2938 
tr_torrentSetDateDone(tr_torrent * tor,time_t t)2939 void tr_torrentSetDateDone(tr_torrent* tor, time_t t)
2940 {
2941     TR_ASSERT(tr_isTorrent(tor));
2942 
2943     tor->doneDate = t;
2944     tor->anyDate = MAX(tor->anyDate, tor->doneDate);
2945 }
2946 
2947 /**
2948 ***
2949 **/
2950 
tr_torrentGetBytesLeftToAllocate(tr_torrent const * tor)2951 uint64_t tr_torrentGetBytesLeftToAllocate(tr_torrent const* tor)
2952 {
2953     TR_ASSERT(tr_isTorrent(tor));
2954 
2955     uint64_t bytesLeft = 0;
2956 
2957     for (tr_file_index_t i = 0; i < tor->info.fileCount; ++i)
2958     {
2959         if (!tor->info.files[i].dnd)
2960         {
2961             tr_sys_path_info info;
2962             uint64_t const length = tor->info.files[i].length;
2963             char* path = tr_torrentFindFile(tor, i);
2964 
2965             bytesLeft += length;
2966 
2967             if (path != NULL && tr_sys_path_get_info(path, 0, &info, NULL) && info.type == TR_SYS_PATH_IS_FILE &&
2968                 info.size <= length)
2969             {
2970                 bytesLeft -= info.size;
2971             }
2972 
2973             tr_free(path);
2974         }
2975     }
2976 
2977     return bytesLeft;
2978 }
2979 
2980 /****
2981 *****  Removing the torrent's local data
2982 ****/
2983 
isJunkFile(char const * base)2984 static bool isJunkFile(char const* base)
2985 {
2986     static char const* files[] =
2987     {
2988         ".DS_Store",
2989         "desktop.ini",
2990         "Thumbs.db"
2991     };
2992 
2993     for (size_t i = 0; i < TR_N_ELEMENTS(files); ++i)
2994     {
2995         if (strcmp(base, files[i]) == 0)
2996         {
2997             return true;
2998         }
2999     }
3000 
3001 #ifdef __APPLE__
3002 
3003     /* check for resource forks. <http://support.apple.com/kb/TA20578> */
3004     if (memcmp(base, "._", 2) == 0)
3005     {
3006         return true;
3007     }
3008 
3009 #endif
3010 
3011     return false;
3012 }
3013 
removeEmptyFoldersAndJunkFiles(char const * folder)3014 static void removeEmptyFoldersAndJunkFiles(char const* folder)
3015 {
3016     tr_sys_dir_t odir;
3017 
3018     if ((odir = tr_sys_dir_open(folder, NULL)) != TR_BAD_SYS_DIR)
3019     {
3020         char const* name;
3021 
3022         while ((name = tr_sys_dir_read_name(odir, NULL)) != NULL)
3023         {
3024             if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0)
3025             {
3026                 tr_sys_path_info info;
3027                 char* filename = tr_buildPath(folder, name, NULL);
3028 
3029                 if (tr_sys_path_get_info(filename, 0, &info, NULL) && info.type == TR_SYS_PATH_IS_DIRECTORY)
3030                 {
3031                     removeEmptyFoldersAndJunkFiles(filename);
3032                 }
3033                 else if (isJunkFile(name))
3034                 {
3035                     tr_sys_path_remove(filename, NULL);
3036                 }
3037 
3038                 tr_free(filename);
3039             }
3040         }
3041 
3042         tr_sys_path_remove(folder, NULL);
3043         tr_sys_dir_close(odir, NULL);
3044     }
3045 }
3046 
3047 /**
3048  * This convoluted code does something (seemingly) simple:
3049  * remove the torrent's local files.
3050  *
3051  * Fun complications:
3052  * 1. Try to preserve the directory hierarchy in the recycle bin.
3053  * 2. If there are nontorrent files, don't delete them...
3054  * 3. ...unless the other files are "junk", such as .DS_Store
3055  */
deleteLocalData(tr_torrent * tor,tr_fileFunc func)3056 static void deleteLocalData(tr_torrent* tor, tr_fileFunc func)
3057 {
3058     char* base;
3059     tr_sys_dir_t odir;
3060     char* tmpdir = NULL;
3061     tr_ptrArray files = TR_PTR_ARRAY_INIT;
3062     tr_ptrArray folders = TR_PTR_ARRAY_INIT;
3063     PtrArrayCompareFunc vstrcmp = (PtrArrayCompareFunc)strcmp;
3064     char const* const top = tor->currentDir;
3065 
3066     /* don't try to delete local data if the directory's gone missing */
3067     if (!tr_sys_path_exists(top, NULL))
3068     {
3069         return;
3070     }
3071 
3072     /* if it's a magnet link, there's nothing to move... */
3073     if (!tr_torrentHasMetadata(tor))
3074     {
3075         return;
3076     }
3077 
3078     /***
3079     ****  Move the local data to a new tmpdir
3080     ***/
3081 
3082     base = tr_strdup_printf("%s__XXXXXX", tr_torrentName(tor));
3083     tmpdir = tr_buildPath(top, base, NULL);
3084     tr_sys_dir_create_temp(tmpdir, NULL);
3085     tr_free(base);
3086 
3087     for (tr_file_index_t f = 0; f < tor->info.fileCount; ++f)
3088     {
3089         char* filename;
3090 
3091         /* try to find the file, looking in the partial and download dirs */
3092         filename = tr_buildPath(top, tor->info.files[f].name, NULL);
3093 
3094         if (!tr_sys_path_exists(filename, NULL))
3095         {
3096             char* partial = tr_torrentBuildPartial(tor, f);
3097             tr_free(filename);
3098             filename = tr_buildPath(top, partial, NULL);
3099             tr_free(partial);
3100 
3101             if (!tr_sys_path_exists(filename, NULL))
3102             {
3103                 tr_free(filename);
3104                 filename = NULL;
3105             }
3106         }
3107 
3108         /* if we found the file, move it */
3109         if (filename != NULL)
3110         {
3111             char* target = tr_buildPath(tmpdir, tor->info.files[f].name, NULL);
3112             tr_moveFile(filename, target, NULL);
3113             tr_ptrArrayAppend(&files, target);
3114             tr_free(filename);
3115         }
3116     }
3117 
3118     /***
3119     ****  Remove tmpdir.
3120     ****
3121     ****  Try deleting the top-level files & folders to preserve
3122     ****  the directory hierarchy in the recycle bin.
3123     ****  If case that fails -- for example, rmdir () doesn't
3124     ****  delete nonempty folders -- go from the bottom up too.
3125     ***/
3126 
3127     /* try deleting the local data's top-level files & folders */
3128     if ((odir = tr_sys_dir_open(tmpdir, NULL)) != TR_BAD_SYS_DIR)
3129     {
3130         char const* name;
3131 
3132         while ((name = tr_sys_dir_read_name(odir, NULL)) != NULL)
3133         {
3134             if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0)
3135             {
3136                 char* file = tr_buildPath(tmpdir, name, NULL);
3137                 (*func)(file, NULL);
3138                 tr_free(file);
3139             }
3140         }
3141 
3142         tr_sys_dir_close(odir, NULL);
3143     }
3144 
3145     /* go from the bottom up */
3146     for (int i = 0, n = tr_ptrArraySize(&files); i < n; ++i)
3147     {
3148         char* walk = tr_strdup(tr_ptrArrayNth(&files, i));
3149 
3150         while (tr_sys_path_exists(walk, NULL) && !tr_sys_path_is_same(tmpdir, walk, NULL))
3151         {
3152             char* tmp = tr_sys_path_dirname(walk, NULL);
3153             (*func)(walk, NULL);
3154             tr_free(walk);
3155             walk = tmp;
3156         }
3157 
3158         tr_free(walk);
3159     }
3160 
3161     /***
3162     ****  The local data has been removed.
3163     ****  What's left in top are empty folders, junk, and user-generated files.
3164     ****  Remove the first two categories and leave the third.
3165     ***/
3166 
3167     /* build a list of 'top's child directories that belong to this torrent */
3168     for (tr_file_index_t f = 0; f < tor->info.fileCount; ++f)
3169     {
3170         char* dir;
3171         char* filename;
3172 
3173         /* get the directory that this file goes in... */
3174         filename = tr_buildPath(top, tor->info.files[f].name, NULL);
3175         dir = tr_sys_path_dirname(filename, NULL);
3176         tr_free(filename);
3177 
3178         if (dir == NULL)
3179         {
3180             continue;
3181         }
3182 
3183         /* walk up the directory tree until we reach 'top' */
3184         if (!tr_sys_path_is_same(top, dir, NULL) && strcmp(top, dir) != 0)
3185         {
3186             for (;;)
3187             {
3188                 char* parent = tr_sys_path_dirname(dir, NULL);
3189 
3190                 if (tr_sys_path_is_same(top, parent, NULL) || strcmp(top, parent) == 0)
3191                 {
3192                     if (tr_ptrArrayFindSorted(&folders, dir, vstrcmp) == NULL)
3193                     {
3194                         tr_ptrArrayInsertSorted(&folders, tr_strdup(dir), vstrcmp);
3195                     }
3196 
3197                     tr_free(parent);
3198                     break;
3199                 }
3200 
3201                 /* walk upwards to parent */
3202                 tr_free(dir);
3203                 dir = parent;
3204             }
3205         }
3206 
3207         tr_free(dir);
3208     }
3209 
3210     for (int i = 0, n = tr_ptrArraySize(&folders); i < n; ++i)
3211     {
3212         removeEmptyFoldersAndJunkFiles(tr_ptrArrayNth(&folders, i));
3213     }
3214 
3215     /* cleanup */
3216     tr_sys_path_remove(tmpdir, NULL);
3217     tr_free(tmpdir);
3218     tr_ptrArrayDestruct(&folders, tr_free);
3219     tr_ptrArrayDestruct(&files, tr_free);
3220 }
3221 
tr_torrentDeleteLocalData(tr_torrent * tor,tr_fileFunc func)3222 static void tr_torrentDeleteLocalData(tr_torrent* tor, tr_fileFunc func)
3223 {
3224     TR_ASSERT(tr_isTorrent(tor));
3225 
3226     if (func == NULL)
3227     {
3228         func = tr_sys_path_remove;
3229     }
3230 
3231     /* close all the files because we're about to delete them */
3232     tr_cacheFlushTorrent(tor->session->cache, tor);
3233     tr_fdTorrentClose(tor->session, tor->uniqueId);
3234 
3235     deleteLocalData(tor, func);
3236 }
3237 
3238 /***
3239 ****
3240 ***/
3241 
3242 struct LocationData
3243 {
3244     bool move_from_old_location;
3245     int volatile* setme_state;
3246     double volatile* setme_progress;
3247     char* location;
3248     tr_torrent* tor;
3249 };
3250 
setLocation(void * vdata)3251 static void setLocation(void* vdata)
3252 {
3253     struct LocationData* data = vdata;
3254     tr_torrent* tor = data->tor;
3255 
3256     TR_ASSERT(tr_isTorrent(tor));
3257 
3258     tr_torrentLock(tor);
3259 
3260     bool err = false;
3261     bool const do_move = data->move_from_old_location;
3262     char const* location = data->location;
3263     double bytesHandled = 0;
3264 
3265     tr_logAddDebug("Moving \"%s\" location from currentDir \"%s\" to \"%s\"", tr_torrentName(tor), tor->currentDir, location);
3266 
3267     tr_sys_dir_create(location, TR_SYS_DIR_CREATE_PARENTS, 0777, NULL);
3268 
3269     if (!tr_sys_path_is_same(location, tor->currentDir, NULL))
3270     {
3271         /* bad idea to move files while they're being verified... */
3272         tr_verifyRemove(tor);
3273 
3274         /* try to move the files.
3275          * FIXME: there are still all kinds of nasty cases, like what
3276          * if the target directory runs out of space halfway through... */
3277         for (tr_file_index_t i = 0; !err && i < tor->info.fileCount; ++i)
3278         {
3279             char* sub;
3280             char const* oldbase;
3281             tr_file const* f = &tor->info.files[i];
3282 
3283             if (tr_torrentFindFile2(tor, i, &oldbase, &sub, NULL))
3284             {
3285                 char* oldpath = tr_buildPath(oldbase, sub, NULL);
3286                 char* newpath = tr_buildPath(location, sub, NULL);
3287 
3288                 tr_logAddDebug("Found file #%d: %s", (int)i, oldpath);
3289 
3290                 if (do_move && !tr_sys_path_is_same(oldpath, newpath, NULL))
3291                 {
3292                     tr_error* error = NULL;
3293 
3294                     tr_logAddTorInfo(tor, "moving \"%s\" to \"%s\"", oldpath, newpath);
3295 
3296                     if (!tr_moveFile(oldpath, newpath, &error))
3297                     {
3298                         err = true;
3299                         tr_logAddTorErr(tor, "error moving \"%s\" to \"%s\": %s", oldpath, newpath, error->message);
3300                         tr_error_free(error);
3301                     }
3302                 }
3303 
3304                 tr_free(newpath);
3305                 tr_free(oldpath);
3306                 tr_free(sub);
3307             }
3308 
3309             if (data->setme_progress != NULL)
3310             {
3311                 bytesHandled += f->length;
3312                 *data->setme_progress = bytesHandled / tor->info.totalSize;
3313             }
3314         }
3315 
3316         if (!err && do_move)
3317         {
3318             /* blow away the leftover subdirectories in the old location */
3319             tr_torrentDeleteLocalData(tor, tr_sys_path_remove);
3320         }
3321     }
3322 
3323     if (!err)
3324     {
3325         /* set the new location and reverify */
3326         tr_torrentSetDownloadDir(tor, location);
3327 
3328         if (do_move)
3329         {
3330             tr_free(tor->incompleteDir);
3331             tor->incompleteDir = NULL;
3332             tor->currentDir = tor->downloadDir;
3333         }
3334     }
3335 
3336     if (data->setme_state != NULL)
3337     {
3338         *data->setme_state = err ? TR_LOC_ERROR : TR_LOC_DONE;
3339     }
3340 
3341     /* cleanup */
3342     tr_torrentUnlock(tor);
3343     tr_free(data->location);
3344     tr_free(data);
3345 }
3346 
tr_torrentSetLocation(tr_torrent * tor,char const * location,bool move_from_old_location,double volatile * setme_progress,int volatile * setme_state)3347 void tr_torrentSetLocation(tr_torrent* tor, char const* location, bool move_from_old_location, double volatile* setme_progress,
3348     int volatile* setme_state)
3349 {
3350     TR_ASSERT(tr_isTorrent(tor));
3351 
3352     if (setme_state != NULL)
3353     {
3354         *setme_state = TR_LOC_MOVING;
3355     }
3356 
3357     if (setme_progress != NULL)
3358     {
3359         *setme_progress = 0;
3360     }
3361 
3362     /* run this in the libtransmission thread */
3363     struct LocationData* data = tr_new(struct LocationData, 1);
3364     data->tor = tor;
3365     data->location = tr_strdup(location);
3366     data->move_from_old_location = move_from_old_location;
3367     data->setme_state = setme_state;
3368     data->setme_progress = setme_progress;
3369     tr_runInEventThread(tor->session, setLocation, data);
3370 }
3371 
3372 /***
3373 ****
3374 ***/
3375 
tr_torrentFileCompleted(tr_torrent * tor,tr_file_index_t fileIndex)3376 static void tr_torrentFileCompleted(tr_torrent* tor, tr_file_index_t fileIndex)
3377 {
3378     char* sub;
3379     char const* base;
3380     tr_info const* inf = &tor->info;
3381     tr_file const* f = &inf->files[fileIndex];
3382     time_t const now = tr_time();
3383 
3384     /* close the file so that we can reopen in read-only mode as needed */
3385     tr_cacheFlushFile(tor->session->cache, tor, fileIndex);
3386     tr_fdFileClose(tor->session, tor, fileIndex);
3387 
3388     /* now that the file is complete and closed, we can start watching its
3389      * mtime timestamp for changes to know if we need to reverify pieces */
3390     for (tr_piece_index_t i = f->firstPiece; i <= f->lastPiece; ++i)
3391     {
3392         inf->pieces[i].timeChecked = now;
3393     }
3394 
3395     /* if the torrent's current filename isn't the same as the one in the
3396      * metadata -- for example, if it had the ".part" suffix appended to
3397      * it until now -- then rename it to match the one in the metadata */
3398     if (tr_torrentFindFile2(tor, fileIndex, &base, &sub, NULL))
3399     {
3400         if (strcmp(sub, f->name) != 0)
3401         {
3402             char* oldpath = tr_buildPath(base, sub, NULL);
3403             char* newpath = tr_buildPath(base, f->name, NULL);
3404             tr_error* error = NULL;
3405 
3406             if (!tr_sys_path_rename(oldpath, newpath, &error))
3407             {
3408                 tr_logAddTorErr(tor, "Error moving \"%s\" to \"%s\": %s", oldpath, newpath, error->message);
3409                 tr_error_free(error);
3410             }
3411 
3412             tr_free(newpath);
3413             tr_free(oldpath);
3414         }
3415 
3416         tr_free(sub);
3417     }
3418 }
3419 
tr_torrentPieceCompleted(tr_torrent * tor,tr_piece_index_t pieceIndex)3420 static void tr_torrentPieceCompleted(tr_torrent* tor, tr_piece_index_t pieceIndex)
3421 {
3422     tr_peerMgrPieceCompleted(tor, pieceIndex);
3423 
3424     /* if this piece completes any file, invoke the fileCompleted func for it */
3425     for (tr_file_index_t i = 0; i < tor->info.fileCount; ++i)
3426     {
3427         tr_file const* file = &tor->info.files[i];
3428 
3429         if (file->firstPiece <= pieceIndex && pieceIndex <= file->lastPiece)
3430         {
3431             if (tr_cpFileIsComplete(&tor->completion, i))
3432             {
3433                 tr_torrentFileCompleted(tor, i);
3434             }
3435         }
3436     }
3437 }
3438 
tr_torrentGotBlock(tr_torrent * tor,tr_block_index_t block)3439 void tr_torrentGotBlock(tr_torrent* tor, tr_block_index_t block)
3440 {
3441     TR_ASSERT(tr_isTorrent(tor));
3442     TR_ASSERT(tr_amInEventThread(tor->session));
3443 
3444     bool const block_is_new = !tr_torrentBlockIsComplete(tor, block);
3445 
3446     if (block_is_new)
3447     {
3448         tr_piece_index_t p;
3449 
3450         tr_cpBlockAdd(&tor->completion, block);
3451         tr_torrentSetDirty(tor);
3452 
3453         p = tr_torBlockPiece(tor, block);
3454 
3455         if (tr_torrentPieceIsComplete(tor, p))
3456         {
3457             tr_logAddTorDbg(tor, "[LAZY] checking just-completed piece %zu", (size_t)p);
3458 
3459             if (tr_torrentCheckPiece(tor, p))
3460             {
3461                 tr_torrentPieceCompleted(tor, p);
3462             }
3463             else
3464             {
3465                 uint32_t const n = tr_torPieceCountBytes(tor, p);
3466                 tr_logAddTorErr(tor, _("Piece %" PRIu32 ", which was just downloaded, failed its checksum test"), p);
3467                 tor->corruptCur += n;
3468                 tor->downloadedCur -= MIN(tor->downloadedCur, n);
3469                 tr_peerMgrGotBadPiece(tor, p);
3470             }
3471         }
3472     }
3473     else
3474     {
3475         uint32_t const n = tr_torBlockCountBytes(tor, block);
3476         tor->downloadedCur -= MIN(tor->downloadedCur, n);
3477         tr_logAddTorDbg(tor, "we have this block already...");
3478     }
3479 }
3480 
3481 /***
3482 ****
3483 ***/
3484 
find_file_in_dir(char const * name,char const * search_dir,char const ** base,char const ** subpath,tr_sys_path_info * file_info)3485 static void find_file_in_dir(char const* name, char const* search_dir, char const** base, char const** subpath,
3486     tr_sys_path_info* file_info)
3487 {
3488     char* filename = tr_buildPath(search_dir, name, NULL);
3489 
3490     if (tr_sys_path_get_info(filename, 0, file_info, NULL))
3491     {
3492         *base = search_dir;
3493         *subpath = name;
3494     }
3495 
3496     tr_free(filename);
3497 }
3498 
tr_torrentFindFile2(tr_torrent const * tor,tr_file_index_t fileNum,char const ** base,char ** subpath,time_t * mtime)3499 bool tr_torrentFindFile2(tr_torrent const* tor, tr_file_index_t fileNum, char const** base, char** subpath, time_t* mtime)
3500 {
3501     TR_ASSERT(tr_isTorrent(tor));
3502     TR_ASSERT(fileNum < tor->info.fileCount);
3503 
3504     char* part = NULL;
3505     tr_file const* file;
3506     char const* b = NULL;
3507     char const* s = NULL;
3508     tr_sys_path_info file_info;
3509 
3510     file = &tor->info.files[fileNum];
3511 
3512     /* look in the download dir... */
3513     if (b == NULL)
3514     {
3515         find_file_in_dir(file->name, tor->downloadDir, &b, &s, &file_info);
3516     }
3517 
3518     /* look in the incomplete dir... */
3519     if (b == NULL && tor->incompleteDir != NULL)
3520     {
3521         find_file_in_dir(file->name, tor->incompleteDir, &b, &s, &file_info);
3522     }
3523 
3524     if (b == NULL)
3525     {
3526         part = tr_torrentBuildPartial(tor, fileNum);
3527     }
3528 
3529     /* look for a .part file in the incomplete dir... */
3530     if (b == NULL && tor->incompleteDir != NULL)
3531     {
3532         find_file_in_dir(part, tor->incompleteDir, &b, &s, &file_info);
3533     }
3534 
3535     /* look for a .part file in the download dir... */
3536     if (b == NULL)
3537     {
3538         find_file_in_dir(part, tor->downloadDir, &b, &s, &file_info);
3539     }
3540 
3541     /* return the results */
3542     if (base != NULL)
3543     {
3544         *base = b;
3545     }
3546 
3547     if (subpath != NULL)
3548     {
3549         *subpath = tr_strdup(s);
3550     }
3551 
3552     if (mtime != NULL)
3553     {
3554         *mtime = file_info.last_modified_at;
3555     }
3556 
3557     /* cleanup */
3558     tr_free(part);
3559     return b != NULL;
3560 }
3561 
tr_torrentFindFile(tr_torrent const * tor,tr_file_index_t fileNum)3562 char* tr_torrentFindFile(tr_torrent const* tor, tr_file_index_t fileNum)
3563 {
3564     char* subpath;
3565     char* ret = NULL;
3566     char const* base;
3567 
3568     if (tr_torrentFindFile2(tor, fileNum, &base, &subpath, NULL))
3569     {
3570         ret = tr_buildPath(base, subpath, NULL);
3571         tr_free(subpath);
3572     }
3573 
3574     return ret;
3575 }
3576 
3577 /* Decide whether we should be looking for files in downloadDir or incompleteDir. */
refreshCurrentDir(tr_torrent * tor)3578 static void refreshCurrentDir(tr_torrent* tor)
3579 {
3580     char const* dir = NULL;
3581 
3582     if (tor->incompleteDir == NULL)
3583     {
3584         dir = tor->downloadDir;
3585     }
3586     else if (!tr_torrentHasMetadata(tor)) /* no files to find */
3587     {
3588         dir = tor->incompleteDir;
3589     }
3590     else if (!tr_torrentFindFile2(tor, 0, &dir, NULL, NULL))
3591     {
3592         dir = tor->incompleteDir;
3593     }
3594 
3595     TR_ASSERT(dir != NULL);
3596     TR_ASSERT(dir == tor->downloadDir || dir == tor->incompleteDir);
3597 
3598     tor->currentDir = dir;
3599 }
3600 
tr_torrentBuildPartial(tr_torrent const * tor,tr_file_index_t fileNum)3601 char* tr_torrentBuildPartial(tr_torrent const* tor, tr_file_index_t fileNum)
3602 {
3603     return tr_strdup_printf("%s.part", tor->info.files[fileNum].name);
3604 }
3605 
3606 /***
3607 ****
3608 ***/
3609 
compareTorrentByQueuePosition(void const * va,void const * vb)3610 static int compareTorrentByQueuePosition(void const* va, void const* vb)
3611 {
3612     tr_torrent const* a = *(tr_torrent const* const*)va;
3613     tr_torrent const* b = *(tr_torrent const* const*)vb;
3614 
3615     return a->queuePosition - b->queuePosition;
3616 }
3617 
3618 #ifdef TR_ENABLE_ASSERTS
3619 
queueIsSequenced(tr_session * session)3620 static bool queueIsSequenced(tr_session* session)
3621 {
3622     int n;
3623     bool is_sequenced;
3624     tr_torrent** torrents;
3625 
3626     n = 0;
3627     torrents = tr_sessionGetTorrents(session, &n);
3628     qsort(torrents, n, sizeof(tr_torrent*), compareTorrentByQueuePosition);
3629 
3630 #if 0
3631 
3632     fprintf(stderr, "%s", "queue: ");
3633 
3634     for (int i = 0; i < n; ++i)
3635     {
3636         fprintf(stderr, "%d ", tmp[i]->queuePosition);
3637     }
3638 
3639     fputc('\n', stderr);
3640 
3641 #endif
3642 
3643     /* test them */
3644     is_sequenced = true;
3645 
3646     for (int i = 0; is_sequenced && i < n; ++i)
3647     {
3648         is_sequenced = torrents[i]->queuePosition == i;
3649     }
3650 
3651     tr_free(torrents);
3652     return is_sequenced;
3653 }
3654 
3655 #endif
3656 
tr_torrentGetQueuePosition(tr_torrent const * tor)3657 int tr_torrentGetQueuePosition(tr_torrent const* tor)
3658 {
3659     return tor->queuePosition;
3660 }
3661 
tr_torrentSetQueuePosition(tr_torrent * tor,int pos)3662 void tr_torrentSetQueuePosition(tr_torrent* tor, int pos)
3663 {
3664     int back = -1;
3665     tr_torrent* walk;
3666     int const old_pos = tor->queuePosition;
3667     time_t const now = tr_time();
3668 
3669     if (pos < 0)
3670     {
3671         pos = 0;
3672     }
3673 
3674     tor->queuePosition = -1;
3675 
3676     walk = NULL;
3677 
3678     while ((walk = tr_torrentNext(tor->session, walk)) != NULL)
3679     {
3680         if (old_pos < pos)
3681         {
3682             if (old_pos <= walk->queuePosition && walk->queuePosition <= pos)
3683             {
3684                 walk->queuePosition--;
3685                 walk->anyDate = now;
3686             }
3687         }
3688 
3689         if (old_pos > pos)
3690         {
3691             if (pos <= walk->queuePosition && walk->queuePosition < old_pos)
3692             {
3693                 walk->queuePosition++;
3694                 walk->anyDate = now;
3695             }
3696         }
3697 
3698         if (back < walk->queuePosition)
3699         {
3700             back = walk->queuePosition;
3701         }
3702     }
3703 
3704     tor->queuePosition = MIN(pos, back + 1);
3705     tor->anyDate = now;
3706 
3707     TR_ASSERT(queueIsSequenced(tor->session));
3708 }
3709 
tr_torrentsQueueMoveTop(tr_torrent ** torrents_in,int n)3710 void tr_torrentsQueueMoveTop(tr_torrent** torrents_in, int n)
3711 {
3712     tr_torrent** torrents = tr_memdup(torrents_in, sizeof(tr_torrent*) * n);
3713     qsort(torrents, n, sizeof(tr_torrent*), compareTorrentByQueuePosition);
3714 
3715     for (int i = n - 1; i >= 0; --i)
3716     {
3717         tr_torrentSetQueuePosition(torrents[i], 0);
3718     }
3719 
3720     tr_free(torrents);
3721 }
3722 
tr_torrentsQueueMoveUp(tr_torrent ** torrents_in,int n)3723 void tr_torrentsQueueMoveUp(tr_torrent** torrents_in, int n)
3724 {
3725     tr_torrent** torrents;
3726 
3727     torrents = tr_memdup(torrents_in, sizeof(tr_torrent*) * n);
3728     qsort(torrents, n, sizeof(tr_torrent*), compareTorrentByQueuePosition);
3729 
3730     for (int i = 0; i < n; ++i)
3731     {
3732         tr_torrentSetQueuePosition(torrents[i], torrents[i]->queuePosition - 1);
3733     }
3734 
3735     tr_free(torrents);
3736 }
3737 
tr_torrentsQueueMoveDown(tr_torrent ** torrents_in,int n)3738 void tr_torrentsQueueMoveDown(tr_torrent** torrents_in, int n)
3739 {
3740     tr_torrent** torrents;
3741 
3742     torrents = tr_memdup(torrents_in, sizeof(tr_torrent*) * n);
3743     qsort(torrents, n, sizeof(tr_torrent*), compareTorrentByQueuePosition);
3744 
3745     for (int i = n - 1; i >= 0; --i)
3746     {
3747         tr_torrentSetQueuePosition(torrents[i], torrents[i]->queuePosition + 1);
3748     }
3749 
3750     tr_free(torrents);
3751 }
3752 
tr_torrentsQueueMoveBottom(tr_torrent ** torrents_in,int n)3753 void tr_torrentsQueueMoveBottom(tr_torrent** torrents_in, int n)
3754 {
3755     tr_torrent** torrents;
3756 
3757     torrents = tr_memdup(torrents_in, sizeof(tr_torrent*) * n);
3758     qsort(torrents, n, sizeof(tr_torrent*), compareTorrentByQueuePosition);
3759 
3760     for (int i = 0; i < n; ++i)
3761     {
3762         tr_torrentSetQueuePosition(torrents[i], INT_MAX);
3763     }
3764 
3765     tr_free(torrents);
3766 }
3767 
torrentSetQueued(tr_torrent * tor,bool queued)3768 static void torrentSetQueued(tr_torrent* tor, bool queued)
3769 {
3770     TR_ASSERT(tr_isTorrent(tor));
3771 
3772     if (tr_torrentIsQueued(tor) != queued)
3773     {
3774         tor->isQueued = queued;
3775         tor->anyDate = tr_time();
3776         tr_torrentSetDirty(tor);
3777     }
3778 }
3779 
tr_torrentSetQueueStartCallback(tr_torrent * torrent,void (* callback)(tr_torrent *,void *),void * user_data)3780 void tr_torrentSetQueueStartCallback(tr_torrent* torrent, void (* callback)(tr_torrent*, void*), void* user_data)
3781 {
3782     torrent->queue_started_callback = callback;
3783     torrent->queue_started_user_data = user_data;
3784 }
3785 
3786 /***
3787 ****
3788 ****  RENAME
3789 ****
3790 ***/
3791 
renameArgsAreValid(char const * oldpath,char const * newname)3792 static bool renameArgsAreValid(char const* oldpath, char const* newname)
3793 {
3794     return !tr_str_is_empty(oldpath) && !tr_str_is_empty(newname) && strcmp(newname, ".") != 0 && strcmp(newname, "..") != 0 &&
3795         strchr(newname, TR_PATH_DELIMITER) == NULL;
3796 }
3797 
renameFindAffectedFiles(tr_torrent * tor,char const * oldpath,size_t * setme_n)3798 static tr_file_index_t* renameFindAffectedFiles(tr_torrent* tor, char const* oldpath, size_t* setme_n)
3799 {
3800     size_t n;
3801     size_t oldpath_len;
3802     tr_file_index_t* indices = tr_new0(tr_file_index_t, tor->info.fileCount);
3803 
3804     n = 0;
3805     oldpath_len = strlen(oldpath);
3806 
3807     for (tr_file_index_t i = 0; i < tor->info.fileCount; ++i)
3808     {
3809         char const* name = tor->info.files[i].name;
3810         size_t const len = strlen(name);
3811 
3812         if ((len == oldpath_len || (len > oldpath_len && name[oldpath_len] == '/')) && memcmp(oldpath, name, oldpath_len) == 0)
3813         {
3814             indices[n++] = i;
3815         }
3816     }
3817 
3818     *setme_n = n;
3819     return indices;
3820 }
3821 
renamePath(tr_torrent * tor,char const * oldpath,char const * newname)3822 static int renamePath(tr_torrent* tor, char const* oldpath, char const* newname)
3823 {
3824     char* src;
3825     char const* base;
3826     int err = 0;
3827 
3828     if (!tr_torrentIsSeed(tor) && tor->incompleteDir != NULL)
3829     {
3830         base = tor->incompleteDir;
3831     }
3832     else
3833     {
3834         base = tor->downloadDir;
3835     }
3836 
3837     src = tr_buildPath(base, oldpath, NULL);
3838 
3839     if (!tr_sys_path_exists(src, NULL)) /* check for it as a partial */
3840     {
3841         char* tmp = tr_strdup_printf("%s.part", src);
3842         tr_free(src);
3843         src = tmp;
3844     }
3845 
3846     if (tr_sys_path_exists(src, NULL))
3847     {
3848         int tmp;
3849         bool tgt_exists;
3850         char* parent = tr_sys_path_dirname(src, NULL);
3851         char* tgt;
3852 
3853         if (tr_str_has_suffix(src, ".part"))
3854         {
3855             tgt = tr_strdup_printf("%s" TR_PATH_DELIMITER_STR "%s.part", parent, newname);
3856         }
3857         else
3858         {
3859             tgt = tr_buildPath(parent, newname, NULL);
3860         }
3861 
3862         tmp = errno;
3863         tgt_exists = tr_sys_path_exists(tgt, NULL);
3864         errno = tmp;
3865 
3866         if (!tgt_exists)
3867         {
3868             tr_error* error = NULL;
3869 
3870             tmp = errno;
3871 
3872             if (!tr_sys_path_rename(src, tgt, &error))
3873             {
3874                 err = error->code;
3875                 tr_error_free(error);
3876             }
3877 
3878             errno = tmp;
3879         }
3880 
3881         tr_free(tgt);
3882         tr_free(parent);
3883     }
3884 
3885     tr_free(src);
3886 
3887     return err;
3888 }
3889 
renameTorrentFileString(tr_torrent * tor,char const * oldpath,char const * newname,tr_file_index_t fileIndex)3890 static void renameTorrentFileString(tr_torrent* tor, char const* oldpath, char const* newname, tr_file_index_t fileIndex)
3891 {
3892     char* name;
3893     tr_file* file = &tor->info.files[fileIndex];
3894     size_t const oldpath_len = strlen(oldpath);
3895 
3896     if (strchr(oldpath, TR_PATH_DELIMITER) == NULL)
3897     {
3898         if (oldpath_len >= strlen(file->name))
3899         {
3900             name = tr_buildPath(newname, NULL);
3901         }
3902         else
3903         {
3904             name = tr_buildPath(newname, file->name + oldpath_len + 1, NULL);
3905         }
3906     }
3907     else
3908     {
3909         char* tmp = tr_sys_path_dirname(oldpath, NULL);
3910 
3911         if (tmp == NULL)
3912         {
3913             return;
3914         }
3915 
3916         if (oldpath_len >= strlen(file->name))
3917         {
3918             name = tr_buildPath(tmp, newname, NULL);
3919         }
3920         else
3921         {
3922             name = tr_buildPath(tmp, newname, file->name + oldpath_len + 1, NULL);
3923         }
3924 
3925         tr_free(tmp);
3926     }
3927 
3928     if (strcmp(file->name, name) == 0)
3929     {
3930         tr_free(name);
3931     }
3932     else
3933     {
3934         tr_free(file->name);
3935         file->name = name;
3936         file->is_renamed = true;
3937     }
3938 }
3939 
3940 struct rename_data
3941 {
3942     tr_torrent* tor;
3943     char* oldpath;
3944     char* newname;
3945     tr_torrent_rename_done_func callback;
3946     void* callback_user_data;
3947 };
3948 
torrentRenamePath(void * vdata)3949 static void torrentRenamePath(void* vdata)
3950 {
3951     struct rename_data* data = vdata;
3952     tr_torrent* const tor = data->tor;
3953 
3954     TR_ASSERT(tr_isTorrent(tor));
3955 
3956     /***
3957     ****
3958     ***/
3959 
3960     int error = 0;
3961     char const* const oldpath = data->oldpath;
3962     char const* const newname = data->newname;
3963 
3964     if (!renameArgsAreValid(oldpath, newname))
3965     {
3966         error = EINVAL;
3967     }
3968     else
3969     {
3970         size_t n;
3971         tr_file_index_t* file_indices;
3972 
3973         file_indices = renameFindAffectedFiles(tor, oldpath, &n);
3974 
3975         if (n == 0)
3976         {
3977             error = EINVAL;
3978         }
3979         else
3980         {
3981             error = renamePath(tor, oldpath, newname);
3982 
3983             if (error == 0)
3984             {
3985                 /* update tr_info.files */
3986                 for (size_t i = 0; i < n; ++i)
3987                 {
3988                     renameTorrentFileString(tor, oldpath, newname, file_indices[i]);
3989                 }
3990 
3991                 /* update tr_info.name if user changed the toplevel */
3992                 if (n == tor->info.fileCount && strchr(oldpath, '/') == NULL)
3993                 {
3994                     tr_free(tor->info.name);
3995                     tor->info.name = tr_strdup(newname);
3996                 }
3997 
3998                 tr_torrentMarkEdited(tor);
3999                 tr_torrentSetDirty(tor);
4000             }
4001         }
4002 
4003         tr_free(file_indices);
4004     }
4005 
4006     /***
4007     ****
4008     ***/
4009 
4010     tor->anyDate = tr_time();
4011 
4012     /* callback */
4013     if (data->callback != NULL)
4014     {
4015         (*data->callback)(tor, data->oldpath, data->newname, error, data->callback_user_data);
4016     }
4017 
4018     /* cleanup */
4019     tr_free(data->oldpath);
4020     tr_free(data->newname);
4021     tr_free(data);
4022 }
4023 
tr_torrentRenamePath(tr_torrent * tor,char const * oldpath,char const * newname,tr_torrent_rename_done_func callback,void * callback_user_data)4024 void tr_torrentRenamePath(tr_torrent* tor, char const* oldpath, char const* newname, tr_torrent_rename_done_func callback,
4025     void* callback_user_data)
4026 {
4027     struct rename_data* data;
4028 
4029     data = tr_new0(struct rename_data, 1);
4030     data->tor = tor;
4031     data->oldpath = tr_strdup(oldpath);
4032     data->newname = tr_strdup(newname);
4033     data->callback = callback;
4034     data->callback_user_data = callback_user_data;
4035 
4036     tr_runInEventThread(tor->session, torrentRenamePath, data);
4037 }
4038