2 * This file Copyright (C) 2009-2017 Mnemosyne LLC
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
9 #include <errno.h> /* EINVAL */
10 #include <signal.h> /* signal() */
13 #include <sys/wait.h> /* wait() */
14 #include <unistd.h> /* fork(), execvp(), _exit() */
16 #include <windows.h> /* CreateProcess(), GetLastError() */
21 #include <string.h> /* memcmp */
22 #include <stdlib.h> /* qsort */
23 #include <limits.h> /* INT_MAX */
25 #include <event2/util.h> /* evutil_vsnprintf() */
27 #include "transmission.h"
28 #include "announcer.h"
29 #include "bandwidth.h"
31 #include "completion.h"
32 #include "crypto-utils.h" /* for tr_sha1 */
34 #include "fdlimit.h" /* tr_fdTorrentClose */
36 #include "inout.h" /* tr_ioTestPiece() */
40 #include "peer-common.h" /* MAX_BLOCK_SIZE */
42 #include "platform.h" /* TR_PATH_DELIMITER_STR */
46 #include "subprocess.h"
48 #include "torrent-magnet.h"
49 #include "tr-assert.h"
50 #include "trevent.h" /* tr_runInEventThread() */
60 #define tr_deeplog_tor(tor, ...) tr_logAddDeepNamed(tr_torrentName(tor), __VA_ARGS__)
66 char const* tr_torrentName(tr_torrent const* tor)
68 return tor != NULL ? tor->info.name : "";
71 int tr_torrentId(tr_torrent const* tor)
73 return tor != NULL ? tor->uniqueId : -1;
76 tr_torrent* tr_torrentFindFromId(tr_session* session, int id)
78 tr_torrent* tor = NULL;
80 while ((tor = tr_torrentNext(session, tor)) != NULL)
82 if (tor->uniqueId == id)
91 tr_torrent* tr_torrentFindFromHashString(tr_session* session, char const* str)
93 tr_torrent* tor = NULL;
95 while ((tor = tr_torrentNext(session, tor)) != NULL)
97 if (!evutil_ascii_strcasecmp(str, tor->info.hashString))
106 tr_torrent* tr_torrentFindFromHash(tr_session* session, uint8_t const* torrentHash)
108 tr_torrent* tor = NULL;
110 while ((tor = tr_torrentNext(session, tor)) != NULL)
112 if (*tor->info.hash == *torrentHash)
114 if (memcmp(tor->info.hash, torrentHash, SHA_DIGEST_LENGTH) == 0)
124 tr_torrent* tr_torrentFindFromMagnetLink(tr_session* session, char const* magnet)
126 tr_magnet_info* info;
127 tr_torrent* tor = NULL;
129 if ((info = tr_magnetParse(magnet)) != NULL)
131 tor = tr_torrentFindFromHash(session, info->hash);
138 tr_torrent* tr_torrentFindFromObfuscatedHash(tr_session* session, uint8_t const* obfuscatedTorrentHash)
140 tr_torrent* tor = NULL;
142 while ((tor = tr_torrentNext(session, tor)) != NULL)
144 if (memcmp(tor->obfuscatedHash, obfuscatedTorrentHash, SHA_DIGEST_LENGTH) == 0)
153 bool tr_torrentIsPieceTransferAllowed(tr_torrent const* tor, tr_direction direction)
155 TR_ASSERT(tr_isTorrent(tor));
156 TR_ASSERT(tr_isDirection(direction));
160 if (tr_torrentUsesSpeedLimit(tor, direction))
162 if (tr_torrentGetSpeedLimit_Bps(tor, direction) <= 0)
168 if (tr_torrentUsesSessionLimits(tor))
172 if (tr_sessionGetActiveSpeedLimit_Bps(tor->session, direction, &limit))
188 static void tr_torrentUnsetPeerId(tr_torrent* tor)
190 /* triggers a rebuild next time tr_torrentGetPeerId() is called */
191 *tor->peer_id = '\0';
194 static int peerIdTTL(tr_torrent const* tor)
198 if (tor->peer_id_creation_time == 0)
204 ttl = (int)difftime(tor->peer_id_creation_time + tor->session->peer_id_ttl_hours * 3600, tr_time());
210 unsigned char const* tr_torrentGetPeerId(tr_torrent* tor)
212 bool needs_new_peer_id = false;
214 if (*tor->peer_id == '\0')
216 needs_new_peer_id = true;
219 if (!needs_new_peer_id)
221 if (!tr_torrentIsPrivate(tor))
223 if (peerIdTTL(tor) <= 0)
225 needs_new_peer_id = true;
230 if (needs_new_peer_id)
232 tr_peerIdInit(tor->peer_id);
233 tor->peer_id_creation_time = tr_time();
239 **** PER-TORRENT UL / DL SPEEDS
242 void tr_torrentSetSpeedLimit_Bps(tr_torrent* tor, tr_direction dir, unsigned int Bps)
244 TR_ASSERT(tr_isTorrent(tor));
245 TR_ASSERT(tr_isDirection(dir));
247 if (tr_bandwidthSetDesiredSpeed_Bps(&tor->bandwidth, dir, Bps))
249 tr_torrentSetDirty(tor);
253 void tr_torrentSetSpeedLimit_KBps(tr_torrent* tor, tr_direction dir, unsigned int KBps)
255 tr_torrentSetSpeedLimit_Bps(tor, dir, toSpeedBytes(KBps));
258 unsigned int tr_torrentGetSpeedLimit_Bps(tr_torrent const* tor, tr_direction dir)
260 TR_ASSERT(tr_isTorrent(tor));
261 TR_ASSERT(tr_isDirection(dir));
263 return tr_bandwidthGetDesiredSpeed_Bps(&tor->bandwidth, dir);
266 unsigned int tr_torrentGetSpeedLimit_KBps(tr_torrent const* tor, tr_direction dir)
268 TR_ASSERT(tr_isTorrent(tor));
269 TR_ASSERT(tr_isDirection(dir));
271 return toSpeedKBps(tr_torrentGetSpeedLimit_Bps(tor, dir));
274 void tr_torrentUseSpeedLimit(tr_torrent* tor, tr_direction dir, bool do_use)
276 TR_ASSERT(tr_isTorrent(tor));
277 TR_ASSERT(tr_isDirection(dir));
279 if (tr_bandwidthSetLimited(&tor->bandwidth, dir, do_use))
281 tr_torrentSetDirty(tor);
285 bool tr_torrentUsesSpeedLimit(tr_torrent const* tor, tr_direction dir)
287 TR_ASSERT(tr_isTorrent(tor));
289 return tr_bandwidthIsLimited(&tor->bandwidth, dir);
292 void tr_torrentUseSessionLimits(tr_torrent* tor, bool doUse)
294 TR_ASSERT(tr_isTorrent(tor));
298 changed = tr_bandwidthHonorParentLimits(&tor->bandwidth, TR_UP, doUse);
299 changed |= tr_bandwidthHonorParentLimits(&tor->bandwidth, TR_DOWN, doUse);
303 tr_torrentSetDirty(tor);
307 bool tr_torrentUsesSessionLimits(tr_torrent const* tor)
309 TR_ASSERT(tr_isTorrent(tor));
311 return tr_bandwidthAreParentLimitsHonored(&tor->bandwidth, TR_UP);
318 void tr_torrentSetRatioMode(tr_torrent* tor, tr_ratiolimit mode)
320 TR_ASSERT(tr_isTorrent(tor));
321 TR_ASSERT(mode == TR_RATIOLIMIT_GLOBAL || mode == TR_RATIOLIMIT_SINGLE || mode == TR_RATIOLIMIT_UNLIMITED);
323 if (mode != tor->ratioLimitMode)
325 tor->ratioLimitMode = mode;
327 tr_torrentSetDirty(tor);
331 tr_ratiolimit tr_torrentGetRatioMode(tr_torrent const* tor)
333 TR_ASSERT(tr_isTorrent(tor));
335 return tor->ratioLimitMode;
338 void tr_torrentSetRatioLimit(tr_torrent* tor, double desiredRatio)
340 TR_ASSERT(tr_isTorrent(tor));
342 if ((int)(desiredRatio * 100.0) != (int)(tor->desiredRatio * 100.0))
344 tor->desiredRatio = desiredRatio;
346 tr_torrentSetDirty(tor);
350 double tr_torrentGetRatioLimit(tr_torrent const* tor)
352 TR_ASSERT(tr_isTorrent(tor));
354 return tor->desiredRatio;
357 bool tr_torrentGetSeedRatio(tr_torrent const* tor, double* ratio)
359 TR_ASSERT(tr_isTorrent(tor));
363 switch (tr_torrentGetRatioMode(tor))
365 case TR_RATIOLIMIT_SINGLE:
370 *ratio = tr_torrentGetRatioLimit(tor);
375 case TR_RATIOLIMIT_GLOBAL:
376 isLimited = tr_sessionIsRatioLimited(tor->session);
378 if (isLimited && ratio != NULL)
380 *ratio = tr_sessionGetRatioLimit(tor->session);
385 default: /* TR_RATIOLIMIT_UNLIMITED */
393 /* returns true if the seed ratio applies --
394 * it applies if the torrent's a seed AND it has a seed ratio set */
395 static bool tr_torrentGetSeedRatioBytes(tr_torrent const* tor, uint64_t* setmeLeft, uint64_t* setmeGoal)
397 TR_ASSERT(tr_isTorrent(tor));
400 bool seedRatioApplies = false;
402 if (tr_torrentGetSeedRatio(tor, &seedRatio))
404 uint64_t const u = tor->uploadedCur + tor->uploadedPrev;
405 uint64_t const d = tor->downloadedCur + tor->downloadedPrev;
406 uint64_t const baseline = d != 0 ? d : tr_cpSizeWhenDone(&tor->completion);
407 uint64_t const goal = baseline * seedRatio;
409 if (setmeLeft != NULL)
411 *setmeLeft = goal > u ? goal - u : 0;
414 if (setmeGoal != NULL)
419 seedRatioApplies = tr_torrentIsSeed(tor);
422 return seedRatioApplies;
425 static bool tr_torrentIsSeedRatioDone(tr_torrent const* tor)
428 return tr_torrentGetSeedRatioBytes(tor, &bytesLeft, NULL) && bytesLeft == 0;
435 void tr_torrentSetIdleMode(tr_torrent* tor, tr_idlelimit mode)
437 TR_ASSERT(tr_isTorrent(tor));
438 TR_ASSERT(mode == TR_IDLELIMIT_GLOBAL || mode == TR_IDLELIMIT_SINGLE || mode == TR_IDLELIMIT_UNLIMITED);
440 if (mode != tor->idleLimitMode)
442 tor->idleLimitMode = mode;
444 tr_torrentSetDirty(tor);
448 tr_idlelimit tr_torrentGetIdleMode(tr_torrent const* tor)
450 TR_ASSERT(tr_isTorrent(tor));
452 return tor->idleLimitMode;
455 void tr_torrentSetIdleLimit(tr_torrent* tor, uint16_t idleMinutes)
457 TR_ASSERT(tr_isTorrent(tor));
461 tor->idleLimitMinutes = idleMinutes;
463 tr_torrentSetDirty(tor);
467 uint16_t tr_torrentGetIdleLimit(tr_torrent const* tor)
469 TR_ASSERT(tr_isTorrent(tor));
471 return tor->idleLimitMinutes;
474 bool tr_torrentGetSeedIdle(tr_torrent const* tor, uint16_t* idleMinutes)
478 switch (tr_torrentGetIdleMode(tor))
480 case TR_IDLELIMIT_SINGLE:
483 if (idleMinutes != NULL)
485 *idleMinutes = tr_torrentGetIdleLimit(tor);
490 case TR_IDLELIMIT_GLOBAL:
491 isLimited = tr_sessionIsIdleLimited(tor->session);
493 if (isLimited && idleMinutes != NULL)
495 *idleMinutes = tr_sessionGetIdleLimit(tor->session);
500 default: /* TR_IDLELIMIT_UNLIMITED */
508 static bool tr_torrentIsSeedIdleLimitDone(tr_torrent* tor)
510 uint16_t idleMinutes;
511 return tr_torrentGetSeedIdle(tor, &idleMinutes) &&
512 difftime(tr_time(), MAX(tor->startDate, tor->activityDate)) >= idleMinutes * 60u;
519 void tr_torrentCheckSeedLimit(tr_torrent* tor)
521 TR_ASSERT(tr_isTorrent(tor));
523 if (!tor->isRunning || tor->isStopping || !tr_torrentIsSeed(tor))
528 /* if we're seeding and reach our seed ratio limit, stop the torrent */
529 if (tr_torrentIsSeedRatioDone(tor))
531 tr_logAddTorInfo(tor, "%s", "Seed ratio reached; pausing torrent");
533 tor->isStopping = true;
535 /* maybe notify the client */
536 if (tor->ratio_limit_hit_func != NULL)
538 (*tor->ratio_limit_hit_func)(tor, tor->ratio_limit_hit_func_user_data);
541 /* if we're seeding and reach our inactiviy limit, stop the torrent */
542 else if (tr_torrentIsSeedIdleLimitDone(tor))
544 tr_logAddTorInfo(tor, "%s", "Seeding idle limit reached; pausing torrent");
546 tor->isStopping = true;
547 tor->finishedSeedingByIdle = true;
549 /* maybe notify the client */
550 if (tor->idle_limit_hit_func != NULL)
552 (*tor->idle_limit_hit_func)(tor, tor->idle_limit_hit_func_user_data);
561 void tr_torrentSetLocalError(tr_torrent* tor, char const* fmt, ...)
563 TR_ASSERT(tr_isTorrent(tor));
568 tor->error = TR_STAT_LOCAL_ERROR;
569 tor->errorTracker[0] = '\0';
570 evutil_vsnprintf(tor->errorString, sizeof(tor->errorString), fmt, ap);
573 tr_logAddTorErr(tor, "%s", tor->errorString);
577 tor->isStopping = true;
581 static void tr_torrentClearError(tr_torrent* tor)
583 tor->error = TR_STAT_OK;
584 tor->errorString[0] = '\0';
585 tor->errorTracker[0] = '\0';
588 static void onTrackerResponse(tr_torrent* tor, tr_tracker_event const* event, void* unused UNUSED)
590 switch (event->messageType)
592 case TR_TRACKER_PEERS:
594 int8_t const seedProbability = event->seedProbability;
595 bool const allAreSeeds = seedProbability == 100;
599 tr_logAddTorDbg(tor, "Got %zu seeds from tracker", event->pexCount);
603 tr_logAddTorDbg(tor, "Got %zu peers from tracker", event->pexCount);
606 for (size_t i = 0; i < event->pexCount; ++i)
608 tr_peerMgrAddPex(tor, TR_PEER_FROM_TRACKER, &event->pex[i], seedProbability);
614 case TR_TRACKER_WARNING:
615 tr_logAddTorErr(tor, _("Tracker warning: \"%s\""), event->text);
616 tor->error = TR_STAT_TRACKER_WARNING;
617 tr_strlcpy(tor->errorTracker, event->tracker, sizeof(tor->errorTracker));
618 tr_strlcpy(tor->errorString, event->text, sizeof(tor->errorString));
621 case TR_TRACKER_ERROR:
622 tr_logAddTorErr(tor, _("Tracker error: \"%s\""), event->text);
623 tor->error = TR_STAT_TRACKER_ERROR;
624 tr_strlcpy(tor->errorTracker, event->tracker, sizeof(tor->errorTracker));
625 tr_strlcpy(tor->errorString, event->text, sizeof(tor->errorString));
628 case TR_TRACKER_ERROR_CLEAR:
629 if (tor->error != TR_STAT_LOCAL_ERROR)
631 tr_torrentClearError(tor);
640 **** TORRENT INSTANTIATION
644 static tr_piece_index_t getBytePiece(tr_info const* info, uint64_t byteOffset)
646 TR_ASSERT(info != NULL);
647 TR_ASSERT(info->pieceSize != 0);
649 tr_piece_index_t piece = byteOffset / info->pieceSize;
651 /* handle 0-byte files at the end of a torrent */
652 if (byteOffset == info->totalSize)
654 piece = info->pieceCount - 1;
660 static void initFilePieces(tr_info* info, tr_file_index_t fileIndex)
662 TR_ASSERT(info != NULL);
663 TR_ASSERT(fileIndex < info->fileCount);
665 tr_file* file = &info->files[fileIndex];
666 uint64_t firstByte = file->offset;
667 uint64_t lastByte = firstByte + (file->length != 0 ? file->length - 1 : 0);
669 file->firstPiece = getBytePiece(info, firstByte);
670 file->lastPiece = getBytePiece(info, lastByte);
673 static bool pieceHasFile(tr_piece_index_t piece, tr_file const* file)
675 return file->firstPiece <= piece && piece <= file->lastPiece;
678 static tr_priority_t calculatePiecePriority(tr_torrent const* tor, tr_piece_index_t piece, int fileHint)
680 tr_file_index_t firstFileIndex;
681 tr_priority_t priority = TR_PRI_LOW;
683 /* find the first file that has data in this piece */
686 firstFileIndex = fileHint;
688 while (firstFileIndex > 0 && pieceHasFile(piece, &tor->info.files[firstFileIndex - 1]))
697 for (tr_file_index_t i = 0; i < tor->info.fileCount; ++i, ++firstFileIndex)
699 if (pieceHasFile(piece, &tor->info.files[i]))
706 /* the piece's priority is the max of the priorities
707 * of all the files in that piece */
708 for (tr_file_index_t i = firstFileIndex; i < tor->info.fileCount; ++i)
710 tr_file const* file = &tor->info.files[i];
712 if (!pieceHasFile(piece, file))
717 priority = MAX(priority, file->priority);
719 /* when dealing with multimedia files, getting the first and
720 last pieces can sometimes allow you to preview it a bit
721 before it's fully downloaded... */
722 if (file->priority >= TR_PRI_NORMAL)
724 if (file->firstPiece == piece || file->lastPiece == piece)
726 priority = TR_PRI_HIGH;
734 static void tr_torrentInitFilePieces(tr_torrent* tor)
737 tr_info* inf = &tor->info;
739 /* assign the file offsets */
740 for (tr_file_index_t f = 0; f < inf->fileCount; ++f)
742 inf->files[f].offset = offset;
743 offset += inf->files[f].length;
744 initFilePieces(inf, f);
747 /* build the array of first-file hints to give calculatePiecePriority */
748 int* firstFiles = tr_new(int, inf->pieceCount);
749 tr_file_index_t f = 0;
751 for (tr_piece_index_t p = 0; p < inf->pieceCount; ++p)
753 while (inf->files[f].lastPiece < p)
763 /* test to confirm the first-file hints are correct */
764 for (tr_piece_index_t p = 0; p < inf->pieceCount; ++p)
766 tr_file_index_t f = firstFiles[p];
768 TR_ASSERT(inf->files[f].firstPiece <= p);
769 TR_ASSERT(inf->files[f].lastPiece >= p);
773 TR_ASSERT(inf->files[f - 1].lastPiece < p);
778 for (tr_file_index_t i = 0; i < inf->fileCount; ++i, ++f)
780 if (pieceHasFile(p, &inf->files[i]))
786 TR_ASSERT((int)f == firstFiles[p]);
791 for (tr_piece_index_t p = 0; p < inf->pieceCount; ++p)
793 inf->pieces[p].priority = calculatePiecePriority(tor, p, firstFiles[p]);
799 static void torrentStart(tr_torrent* tor, bool bypass_queue);
802 * Decide on a block size. Constraints:
803 * (1) most clients decline requests over 16 KiB
804 * (2) pieceSize must be a multiple of block size
806 uint32_t tr_getBlockSize(uint32_t pieceSize)
808 uint32_t b = pieceSize;
810 while (b > MAX_BLOCK_SIZE)
815 if (b == 0 || pieceSize % b != 0) /* not cleanly divisible */
823 static void refreshCurrentDir(tr_torrent* tor);
825 static void torrentInitFromInfo(tr_torrent* tor)
828 tr_info* info = &tor->info;
830 tor->blockSize = tr_getBlockSize(info->pieceSize);
832 if (info->pieceSize != 0)
834 tor->lastPieceSize = (uint32_t)(info->totalSize % info->pieceSize);
837 if (tor->lastPieceSize == 0)
839 tor->lastPieceSize = info->pieceSize;
842 if (tor->blockSize != 0)
844 tor->lastBlockSize = info->totalSize % tor->blockSize;
847 if (tor->lastBlockSize == 0)
849 tor->lastBlockSize = tor->blockSize;
852 tor->blockCount = tor->blockSize != 0 ? (info->totalSize + tor->blockSize - 1) / tor->blockSize : 0;
853 tor->blockCountInPiece = tor->blockSize != 0 ? info->pieceSize / tor->blockSize : 0;
854 tor->blockCountInLastPiece = tor->blockSize != 0 ? (tor->lastPieceSize + tor->blockSize - 1) / tor->blockSize : 0;
857 if (tor->blockSize != 0)
859 TR_ASSERT(info->pieceSize % tor->blockSize == 0);
862 t = info->pieceCount - 1;
863 t *= info->pieceSize;
864 t += tor->lastPieceSize;
865 TR_ASSERT(t == info->totalSize);
867 t = tor->blockCount - 1;
869 t += tor->lastBlockSize;
870 TR_ASSERT(t == info->totalSize);
872 t = info->pieceCount - 1;
873 t *= tor->blockCountInPiece;
874 t += tor->blockCountInLastPiece;
875 TR_ASSERT(t == (uint64_t)tor->blockCount);
877 tr_cpConstruct(&tor->completion, tor);
879 tr_torrentInitFilePieces(tor);
881 tor->completeness = tr_cpGetStatus(&tor->completion);
884 static void tr_torrentFireMetadataCompleted(tr_torrent* tor);
886 void tr_torrentGotNewInfoDict(tr_torrent* tor)
888 torrentInitFromInfo(tor);
890 tr_peerMgrOnTorrentGotMetainfo(tor);
892 tr_torrentFireMetadataCompleted(tor);
895 static bool hasAnyLocalData(tr_torrent const* tor)
897 for (tr_file_index_t i = 0; i < tor->info.fileCount; ++i)
899 if (tr_torrentFindFile2(tor, i, NULL, NULL, NULL))
908 static bool setLocalErrorIfFilesDisappeared(tr_torrent* tor)
910 bool const disappeared = tr_torrentHaveTotal(tor) > 0 && !hasAnyLocalData(tor);
914 tr_deeplog_tor(tor, "%s", "[LAZY] uh oh, the files disappeared");
915 tr_torrentSetLocalError(tor, "%s", _("No data found! Ensure your drives are connected or use \"Set Location\". "
916 "To re-download, remove the torrent and re-add it."));
922 static void torrentInit(tr_torrent* tor, tr_ctor const* ctor)
924 tr_session* session = tr_ctorGetSession(ctor);
926 TR_ASSERT(session != NULL);
928 tr_sessionLock(session);
934 static int nextUniqueId = 1;
936 tor->session = session;
937 tor->uniqueId = nextUniqueId++;
938 tor->magicNumber = TORRENT_MAGIC_NUMBER;
939 tor->queuePosition = session->torrentCount;
941 tr_sha1(tor->obfuscatedHash, "req2", 4, tor->info.hash, SHA_DIGEST_LENGTH, NULL);
943 if (tr_ctorGetDownloadDir(ctor, TR_FORCE, &dir) || tr_ctorGetDownloadDir(ctor, TR_FALLBACK, &dir))
945 tor->downloadDir = tr_strdup(dir);
948 if (!tr_ctorGetIncompleteDir(ctor, &dir))
950 dir = tr_sessionGetIncompleteDir(session);
953 if (tr_sessionIsIncompleteDirEnabled(session))
955 tor->incompleteDir = tr_strdup(dir);
958 tr_bandwidthConstruct(&tor->bandwidth, session, &session->bandwidth);
960 tor->bandwidth.priority = tr_ctorGetBandwidthPriority(ctor);
961 tor->error = TR_STAT_OK;
962 tor->finishedSeedingByIdle = false;
964 tr_peerMgrAddTorrent(session->peerMgr, tor);
966 TR_ASSERT(tor->downloadedCur == 0);
967 TR_ASSERT(tor->uploadedCur == 0);
969 tr_torrentSetAddedDate(tor, tr_time()); /* this is a default value to be overwritten by the resume file */
971 torrentInitFromInfo(tor);
973 bool didRenameResumeFileToHashOnlyName = false;
974 loaded = tr_torrentLoadResume(tor, ~0, ctor, &didRenameResumeFileToHashOnlyName);
976 if (didRenameResumeFileToHashOnlyName)
978 /* Rename torrent file as well */
979 tr_metainfoMigrateFile(session, &tor->info, TR_METAINFO_BASENAME_NAME_AND_PARTIAL_HASH, TR_METAINFO_BASENAME_HASH);
982 tor->completeness = tr_cpGetStatus(&tor->completion);
983 setLocalErrorIfFilesDisappeared(tor);
985 tr_ctorInitTorrentPriorities(ctor, tor);
986 tr_ctorInitTorrentWanted(ctor, tor);
988 refreshCurrentDir(tor);
990 doStart = tor->isRunning;
991 tor->isRunning = false;
993 if ((loaded & TR_FR_SPEEDLIMIT) == 0)
995 tr_torrentUseSpeedLimit(tor, TR_UP, false);
996 tr_torrentSetSpeedLimit_Bps(tor, TR_UP, tr_sessionGetSpeedLimit_Bps(tor->session, TR_UP));
997 tr_torrentUseSpeedLimit(tor, TR_DOWN, false);
998 tr_torrentSetSpeedLimit_Bps(tor, TR_DOWN, tr_sessionGetSpeedLimit_Bps(tor->session, TR_DOWN));
999 tr_torrentUseSessionLimits(tor, true);
1002 if ((loaded & TR_FR_RATIOLIMIT) == 0)
1004 tr_torrentSetRatioMode(tor, TR_RATIOLIMIT_GLOBAL);
1005 tr_torrentSetRatioLimit(tor, tr_sessionGetRatioLimit(tor->session));
1008 if ((loaded & TR_FR_IDLELIMIT) == 0)
1010 tr_torrentSetIdleMode(tor, TR_IDLELIMIT_GLOBAL);
1011 tr_torrentSetIdleLimit(tor, tr_sessionGetIdleLimit(tor->session));
1014 /* add the torrent to tr_session.torrentList */
1015 session->torrentCount++;
1017 if (session->torrentList == NULL)
1019 session->torrentList = tor;
1023 tr_torrent* it = session->torrentList;
1025 while (it->next != NULL)
1033 /* if we don't have a local .torrent file already, assume the torrent is new */
1034 isNewTorrent = !tr_sys_path_exists(tor->info.torrent, NULL);
1036 /* maybe save our own copy of the metainfo */
1037 if (tr_ctorGetSave(ctor))
1039 tr_variant const* val;
1041 if (tr_ctorGetMetainfo(ctor, &val))
1043 char const* path = tor->info.torrent;
1044 int const err = tr_variantToFile(val, TR_VARIANT_FMT_BENC, path);
1048 tr_torrentSetLocalError(tor, "Unable to save torrent file: %s", tr_strerror(err));
1051 tr_sessionSetTorrentFile(tor->session, tor->info.hashString, path);
1055 tor->tiers = tr_announcerAddTorrent(tor, onTrackerResponse, NULL);
1059 tor->startAfterVerify = doStart;
1060 tr_torrentVerify(tor, NULL, NULL);
1064 tr_torrentStart(tor);
1067 tr_sessionUnlock(session);
1070 static tr_parse_result torrentParseImpl(tr_ctor const* ctor, tr_info* setmeInfo, bool* setmeHasInfo, size_t* dictLength,
1071 int* setme_duplicate_id)
1075 bool hasInfo = false;
1077 tr_variant const* metainfo;
1078 tr_session* session = tr_ctorGetSession(ctor);
1079 tr_parse_result result = TR_PARSE_OK;
1081 if (setmeInfo == NULL)
1086 memset(setmeInfo, 0, sizeof(tr_info));
1088 if (!tr_ctorGetMetainfo(ctor, &metainfo))
1090 return TR_PARSE_ERR;
1093 didParse = tr_metainfoParse(session, metainfo, setmeInfo, &hasInfo, dictLength);
1094 doFree = didParse && (setmeInfo == &tmp);
1098 result = TR_PARSE_ERR;
1101 if (didParse && hasInfo && !tr_getBlockSize(setmeInfo->pieceSize))
1103 result = TR_PARSE_ERR;
1106 if (didParse && session != NULL && result == TR_PARSE_OK)
1108 tr_torrent const* const tor = tr_torrentFindFromHash(session, setmeInfo->hash);
1112 result = TR_PARSE_DUPLICATE;
1114 if (setme_duplicate_id != NULL)
1116 *setme_duplicate_id = tr_torrentId(tor);
1123 tr_metainfoFree(setmeInfo);
1126 if (setmeHasInfo != NULL)
1128 *setmeHasInfo = hasInfo;
1134 tr_parse_result tr_torrentParse(tr_ctor const* ctor, tr_info* setmeInfo)
1136 return torrentParseImpl(ctor, setmeInfo, NULL, NULL, NULL);
1139 tr_torrent* tr_torrentNew(tr_ctor const* ctor, int* setme_error, int* setme_duplicate_id)
1141 TR_ASSERT(ctor != NULL);
1142 TR_ASSERT(tr_isSession(tr_ctorGetSession(ctor)));
1148 tr_torrent* tor = NULL;
1150 r = torrentParseImpl(ctor, &tmpInfo, &hasInfo, &len, setme_duplicate_id);
1152 if (r == TR_PARSE_OK)
1154 tor = tr_new0(tr_torrent, 1);
1155 tor->info = tmpInfo;
1159 tor->infoDictLength = len;
1162 torrentInit(tor, ctor);
1166 if (r == TR_PARSE_DUPLICATE)
1168 tr_metainfoFree(&tmpInfo);
1171 if (setme_error != NULL)
1184 void tr_torrentSetDownloadDir(tr_torrent* tor, char const* path)
1186 TR_ASSERT(tr_isTorrent(tor));
1188 if (path == NULL || tor->downloadDir == NULL || strcmp(path, tor->downloadDir) != 0)
1190 tr_free(tor->downloadDir);
1191 tor->downloadDir = tr_strdup(path);
1192 tr_torrentSetDirty(tor);
1195 refreshCurrentDir(tor);
1198 char const* tr_torrentGetDownloadDir(tr_torrent const* tor)
1200 TR_ASSERT(tr_isTorrent(tor));
1202 return tor->downloadDir;
1205 char const* tr_torrentGetCurrentDir(tr_torrent const* tor)
1207 TR_ASSERT(tr_isTorrent(tor));
1209 return tor->currentDir;
1212 void tr_torrentChangeMyPort(tr_torrent* tor)
1214 TR_ASSERT(tr_isTorrent(tor));
1218 tr_announcerChangeMyPort(tor);
1222 static inline void tr_torrentManualUpdateImpl(void* vtor)
1224 tr_torrent* tor = vtor;
1226 TR_ASSERT(tr_isTorrent(tor));
1230 tr_announcerManualAnnounce(tor);
1234 void tr_torrentManualUpdate(tr_torrent* tor)
1236 TR_ASSERT(tr_isTorrent(tor));
1238 tr_runInEventThread(tor->session, tr_torrentManualUpdateImpl, tor);
1241 bool tr_torrentCanManualUpdate(tr_torrent const* tor)
1243 return tr_isTorrent(tor) && tor->isRunning && tr_announcerCanManualAnnounce(tor);
1246 tr_info const* tr_torrentInfo(tr_torrent const* tor)
1248 return tr_isTorrent(tor) ? &tor->info : NULL;
1251 tr_stat const* tr_torrentStatCached(tr_torrent* tor)
1253 time_t const now = tr_time();
1255 return (tr_isTorrent(tor) && now == tor->lastStatTime) ? &tor->stats : tr_torrentStat(tor);
1258 void tr_torrentSetVerifyState(tr_torrent* tor, tr_verify_state state)
1260 TR_ASSERT(tr_isTorrent(tor));
1261 TR_ASSERT(state == TR_VERIFY_NONE || state == TR_VERIFY_WAIT || state == TR_VERIFY_NOW);
1263 tor->verifyState = state;
1264 tor->anyDate = tr_time();
1267 tr_torrent_activity tr_torrentGetActivity(tr_torrent const* tor)
1269 tr_torrent_activity ret = TR_STATUS_STOPPED;
1271 bool const is_seed = tr_torrentIsSeed(tor);
1273 if (tor->verifyState == TR_VERIFY_NOW)
1275 ret = TR_STATUS_CHECK;
1277 else if (tor->verifyState == TR_VERIFY_WAIT)
1279 ret = TR_STATUS_CHECK_WAIT;
1281 else if (tor->isRunning)
1283 ret = is_seed ? TR_STATUS_SEED : TR_STATUS_DOWNLOAD;
1285 else if (tr_torrentIsQueued(tor))
1287 if (is_seed && tr_sessionGetQueueEnabled(tor->session, TR_UP))
1289 ret = TR_STATUS_SEED_WAIT;
1291 else if (!is_seed && tr_sessionGetQueueEnabled(tor->session, TR_DOWN))
1293 ret = TR_STATUS_DOWNLOAD_WAIT;
1300 static time_t torrentGetIdleSecs(tr_torrent const* tor)
1303 tr_torrent_activity const activity = tr_torrentGetActivity(tor);
1305 if ((activity == TR_STATUS_DOWNLOAD || activity == TR_STATUS_SEED) && tor->startDate != 0)
1307 idle_secs = difftime(tr_time(), MAX(tor->startDate, tor->activityDate));
1317 bool tr_torrentIsStalled(tr_torrent const* tor)
1319 return tr_sessionGetQueueStalledEnabled(tor->session) &&
1320 torrentGetIdleSecs(tor) > tr_sessionGetQueueStalledMinutes(tor->session) * 60;
1323 static double getVerifyProgress(tr_torrent const* tor)
1327 if (tr_torrentHasMetadata(tor))
1329 tr_piece_index_t checked = 0;
1331 for (tr_piece_index_t i = 0; i < tor->info.pieceCount; ++i)
1333 if (tor->info.pieces[i].timeChecked != 0)
1339 d = checked / (double)tor->info.pieceCount;
1345 tr_stat const* tr_torrentStat(tr_torrent* tor)
1347 TR_ASSERT(tr_isTorrent(tor));
1349 uint64_t const now = tr_time_msec();
1352 uint64_t seedRatioBytesLeft;
1353 uint64_t seedRatioBytesGoal;
1354 bool seedRatioApplies;
1355 uint16_t seedIdleMinutes;
1356 unsigned int pieceUploadSpeed_Bps;
1357 unsigned int pieceDownloadSpeed_Bps;
1358 struct tr_swarm_stats swarm_stats;
1360 tor->lastStatTime = tr_time();
1362 if (tor->swarm != NULL)
1364 tr_swarmGetStats(tor->swarm, &swarm_stats);
1368 swarm_stats = TR_SWARM_STATS_INIT;
1372 s->id = tor->uniqueId;
1373 s->activity = tr_torrentGetActivity(tor);
1374 s->error = tor->error;
1375 s->queuePosition = tor->queuePosition;
1376 s->isStalled = tr_torrentIsStalled(tor);
1377 tr_strlcpy(s->errorString, tor->errorString, sizeof(s->errorString));
1379 s->manualAnnounceTime = tr_announcerNextManualAnnounce(tor);
1380 s->peersConnected = swarm_stats.peerCount;
1381 s->peersSendingToUs = swarm_stats.activePeerCount[TR_DOWN];
1382 s->peersGettingFromUs = swarm_stats.activePeerCount[TR_UP];
1383 s->webseedsSendingToUs = swarm_stats.activeWebseedCount;
1385 for (int i = 0; i < TR_PEER_FROM__MAX; i++)
1387 s->peersFrom[i] = swarm_stats.peerFromCount[i];
1390 s->rawUploadSpeed_KBps = toSpeedKBps(tr_bandwidthGetRawSpeed_Bps(&tor->bandwidth, now, TR_UP));
1391 s->rawDownloadSpeed_KBps = toSpeedKBps(tr_bandwidthGetRawSpeed_Bps(&tor->bandwidth, now, TR_DOWN));
1392 pieceUploadSpeed_Bps = tr_bandwidthGetPieceSpeed_Bps(&tor->bandwidth, now, TR_UP);
1393 pieceDownloadSpeed_Bps = tr_bandwidthGetPieceSpeed_Bps(&tor->bandwidth, now, TR_DOWN);
1394 s->pieceUploadSpeed_KBps = toSpeedKBps(pieceUploadSpeed_Bps);
1395 s->pieceDownloadSpeed_KBps = toSpeedKBps(pieceDownloadSpeed_Bps);
1397 s->percentComplete = tr_cpPercentComplete(&tor->completion);
1398 s->metadataPercentComplete = tr_torrentGetMetadataPercent(tor);
1400 s->percentDone = tr_cpPercentDone(&tor->completion);
1401 s->leftUntilDone = tr_torrentGetLeftUntilDone(tor);
1402 s->sizeWhenDone = tr_cpSizeWhenDone(&tor->completion);
1403 s->recheckProgress = s->activity == TR_STATUS_CHECK ? getVerifyProgress(tor) : 0;
1404 s->activityDate = tor->activityDate;
1405 s->addedDate = tor->addedDate;
1406 s->doneDate = tor->doneDate;
1407 s->startDate = tor->startDate;
1408 s->secondsSeeding = tor->secondsSeeding;
1409 s->secondsDownloading = tor->secondsDownloading;
1410 s->idleSecs = torrentGetIdleSecs(tor);
1412 s->corruptEver = tor->corruptCur + tor->corruptPrev;
1413 s->downloadedEver = tor->downloadedCur + tor->downloadedPrev;
1414 s->uploadedEver = tor->uploadedCur + tor->uploadedPrev;
1415 s->haveValid = tr_cpHaveValid(&tor->completion);
1416 s->haveUnchecked = tr_torrentHaveTotal(tor) - s->haveValid;
1417 s->desiredAvailable = tr_peerMgrGetDesiredAvailable(tor);
1419 s->ratio = tr_getRatio(s->uploadedEver, s->downloadedEver ? s->downloadedEver : s->haveValid);
1421 seedRatioApplies = tr_torrentGetSeedRatioBytes(tor, &seedRatioBytesLeft, &seedRatioBytesGoal);
1423 switch (s->activity)
1425 /* etaXLSpeed exists because if we use the piece speed directly,
1426 * brief fluctuations cause the ETA to jump all over the place.
1427 * so, etaXLSpeed is a smoothed-out version of the piece speed
1428 * to dampen the effect of fluctuations */
1429 case TR_STATUS_DOWNLOAD:
1430 if (tor->etaDLSpeedCalculatedAt + 800 < now)
1432 tor->etaDLSpeed_Bps = tor->etaDLSpeedCalculatedAt + 4000 < now ?
1433 pieceDownloadSpeed_Bps : /* if no recent previous speed, no need to smooth */
1434 (tor->etaDLSpeed_Bps * 4.0 + pieceDownloadSpeed_Bps) / 5.0; /* smooth across 5 readings */
1435 tor->etaDLSpeedCalculatedAt = now;
1438 if (s->leftUntilDone > s->desiredAvailable && tor->info.webseedCount < 1)
1440 s->eta = TR_ETA_NOT_AVAIL;
1442 else if (tor->etaDLSpeed_Bps == 0)
1444 s->eta = TR_ETA_UNKNOWN;
1448 s->eta = s->leftUntilDone / tor->etaDLSpeed_Bps;
1451 s->etaIdle = TR_ETA_NOT_AVAIL;
1454 case TR_STATUS_SEED:
1455 if (!seedRatioApplies)
1457 s->eta = TR_ETA_NOT_AVAIL;
1461 if (tor->etaULSpeedCalculatedAt + 800 < now)
1463 tor->etaULSpeed_Bps = tor->etaULSpeedCalculatedAt + 4000 < now ?
1464 pieceUploadSpeed_Bps : /* if no recent previous speed, no need to smooth */
1465 (tor->etaULSpeed_Bps * 4.0 + pieceUploadSpeed_Bps) / 5.0; /* smooth across 5 readings */
1466 tor->etaULSpeedCalculatedAt = now;
1469 if (tor->etaULSpeed_Bps == 0)
1471 s->eta = TR_ETA_UNKNOWN;
1475 s->eta = seedRatioBytesLeft / tor->etaULSpeed_Bps;
1479 if (tor->etaULSpeed_Bps < 1 && tr_torrentGetSeedIdle(tor, &seedIdleMinutes))
1481 s->etaIdle = seedIdleMinutes * 60 - s->idleSecs;
1485 s->etaIdle = TR_ETA_NOT_AVAIL;
1491 s->eta = TR_ETA_NOT_AVAIL;
1492 s->etaIdle = TR_ETA_NOT_AVAIL;
1496 /* s->haveValid is here to make sure a torrent isn't marked 'finished'
1497 * when the user hits "uncheck all" prior to starting the torrent... */
1498 s->finished = tor->finishedSeedingByIdle || (seedRatioApplies && seedRatioBytesLeft == 0 && s->haveValid != 0);
1500 if (!seedRatioApplies || s->finished)
1502 s->seedRatioPercentDone = 1;
1504 else if (seedRatioBytesGoal == 0) /* impossible? safeguard for div by zero */
1506 s->seedRatioPercentDone = 0;
1510 s->seedRatioPercentDone = (double)(seedRatioBytesGoal - seedRatioBytesLeft) / seedRatioBytesGoal;
1513 /* test some of the constraints */
1514 TR_ASSERT(s->sizeWhenDone <= tor->info.totalSize);
1515 TR_ASSERT(s->leftUntilDone <= s->sizeWhenDone);
1516 TR_ASSERT(s->desiredAvailable <= s->leftUntilDone);
1525 static uint64_t countFileBytesCompleted(tr_torrent const* tor, tr_file_index_t index)
1528 tr_file const* f = &tor->info.files[index];
1532 tr_block_index_t first;
1533 tr_block_index_t last;
1534 tr_torGetFileBlockRange(tor, index, &first, &last);
1538 if (tr_torrentBlockIsComplete(tor, first))
1545 /* the first block */
1546 if (tr_torrentBlockIsComplete(tor, first))
1548 total += tor->blockSize - f->offset % tor->blockSize;
1551 /* the middle blocks */
1552 if (first + 1 < last)
1554 uint64_t u = tr_bitfieldCountRange(&tor->completion.blockBitfield, first + 1, last);
1555 u *= tor->blockSize;
1559 /* the last block */
1560 if (tr_torrentBlockIsComplete(tor, last))
1562 total += f->offset + f->length - (uint64_t)tor->blockSize * last;
1570 tr_file_stat* tr_torrentFiles(tr_torrent const* tor, tr_file_index_t* fileCount)
1572 TR_ASSERT(tr_isTorrent(tor));
1574 tr_file_index_t const n = tor->info.fileCount;
1575 tr_file_stat* files = tr_new0(tr_file_stat, n);
1576 tr_file_stat* walk = files;
1577 bool const isSeed = tor->completeness == TR_SEED;
1579 for (tr_file_index_t i = 0; i < n; ++i, ++walk)
1581 uint64_t const b = isSeed ? tor->info.files[i].length : countFileBytesCompleted(tor, i);
1582 walk->bytesCompleted = b;
1583 walk->progress = tor->info.files[i].length > 0 ? (float)b / tor->info.files[i].length : 1.0f;
1586 if (fileCount != NULL)
1594 void tr_torrentFilesFree(tr_file_stat* files, tr_file_index_t fileCount UNUSED)
1603 double* tr_torrentWebSpeeds_KBps(tr_torrent const* tor)
1605 TR_ASSERT(tr_isTorrent(tor));
1607 return tr_peerMgrWebSpeeds_KBps(tor);
1610 tr_peer_stat* tr_torrentPeers(tr_torrent const* tor, int* peerCount)
1612 TR_ASSERT(tr_isTorrent(tor));
1614 return tr_peerMgrPeerStats(tor, peerCount);
1617 void tr_torrentPeersFree(tr_peer_stat* peers, int peerCount UNUSED)
1622 tr_tracker_stat* tr_torrentTrackers(tr_torrent const* tor, int* setmeTrackerCount)
1624 TR_ASSERT(tr_isTorrent(tor));
1626 return tr_announcerStats(tor, setmeTrackerCount);
1629 void tr_torrentTrackersFree(tr_tracker_stat* trackers, int trackerCount)
1631 tr_announcerStatsFree(trackers, trackerCount);
1634 void tr_torrentAvailability(tr_torrent const* tor, int8_t* tab, int size)
1636 TR_ASSERT(tr_isTorrent(tor));
1638 if (tab != NULL && size > 0)
1640 tr_peerMgrTorrentAvailability(tor, tab, size);
1644 void tr_torrentAmountFinished(tr_torrent const* tor, float* tab, int size)
1646 tr_cpGetAmountDone(&tor->completion, tab, size);
1649 static void tr_torrentResetTransferStats(tr_torrent* tor)
1651 tr_torrentLock(tor);
1653 tor->downloadedPrev += tor->downloadedCur;
1654 tor->downloadedCur = 0;
1655 tor->uploadedPrev += tor->uploadedCur;
1656 tor->uploadedCur = 0;
1657 tor->corruptPrev += tor->corruptCur;
1658 tor->corruptCur = 0;
1660 tr_torrentSetDirty(tor);
1662 tr_torrentUnlock(tor);
1665 void tr_torrentSetHasPiece(tr_torrent* tor, tr_piece_index_t pieceIndex, bool has)
1667 TR_ASSERT(tr_isTorrent(tor));
1668 TR_ASSERT(pieceIndex < tor->info.pieceCount);
1672 tr_cpPieceAdd(&tor->completion, pieceIndex);
1676 tr_cpPieceRem(&tor->completion, pieceIndex);
1684 #ifdef TR_ENABLE_ASSERTS
1685 static bool queueIsSequenced(tr_session*);
1688 static void freeTorrent(tr_torrent* tor)
1690 TR_ASSERT(!tor->isRunning);
1692 tr_session* session = tor->session;
1693 tr_info* inf = &tor->info;
1694 time_t const now = tr_time();
1696 tr_sessionLock(session);
1698 tr_peerMgrRemoveTorrent(tor);
1700 tr_announcerRemoveTorrent(session->announcer, tor);
1702 tr_cpDestruct(&tor->completion);
1704 tr_free(tor->downloadDir);
1705 tr_free(tor->incompleteDir);
1707 if (tor == session->torrentList)
1709 session->torrentList = tor->next;
1713 for (tr_torrent* t = session->torrentList; t != NULL; t = t->next)
1717 t->next = tor->next;
1723 /* decrement the torrent count */
1724 TR_ASSERT(session->torrentCount >= 1);
1725 session->torrentCount--;
1727 /* resequence the queue positions */
1728 tr_torrent* t = NULL;
1730 while ((t = tr_torrentNext(session, t)) != NULL)
1732 if (t->queuePosition > tor->queuePosition)
1739 TR_ASSERT(queueIsSequenced(session));
1741 tr_bandwidthDestruct(&tor->bandwidth);
1743 tr_metainfoFree(inf);
1744 memset(tor, ~0, sizeof(tr_torrent));
1747 tr_sessionUnlock(session);
1751 *** Start/Stop Callback
1754 static void torrentSetQueued(tr_torrent* tor, bool queued);
1756 static void torrentStartImpl(void* vtor)
1758 tr_torrent* tor = vtor;
1760 TR_ASSERT(tr_isTorrent(tor));
1762 tr_sessionLock(tor->session);
1764 tr_torrentRecheckCompleteness(tor);
1765 torrentSetQueued(tor, false);
1767 time_t const now = tr_time();
1769 tor->isRunning = true;
1770 tor->completeness = tr_cpGetStatus(&tor->completion);
1771 tor->startDate = now;
1773 tr_torrentClearError(tor);
1774 tor->finishedSeedingByIdle = false;
1776 tr_torrentResetTransferStats(tor);
1777 tr_announcerTorrentStarted(tor);
1778 tor->dhtAnnounceAt = now + tr_rand_int_weak(20);
1779 tor->dhtAnnounce6At = now + tr_rand_int_weak(20);
1780 tor->lpdAnnounceAt = now;
1781 tr_peerMgrStartTorrent(tor);
1783 tr_sessionUnlock(tor->session);
1786 uint64_t tr_torrentGetCurrentSizeOnDisk(tr_torrent const* tor)
1788 uint64_t byte_count = 0;
1789 tr_file_index_t const n = tor->info.fileCount;
1791 for (tr_file_index_t i = 0; i < n; ++i)
1793 tr_sys_path_info info;
1794 char* filename = tr_torrentFindFile(tor, i);
1796 if (filename != NULL && tr_sys_path_get_info(filename, 0, &info, NULL))
1798 byte_count += info.size;
1807 static bool torrentShouldQueue(tr_torrent const* tor)
1809 tr_direction const dir = tr_torrentGetQueueDirection(tor);
1811 return tr_sessionCountQueueFreeSlots(tor->session, dir) == 0;
1814 static void torrentStart(tr_torrent* tor, bool bypass_queue)
1816 switch (tr_torrentGetActivity(tor))
1818 case TR_STATUS_SEED:
1819 case TR_STATUS_DOWNLOAD:
1820 return; /* already started */
1823 case TR_STATUS_SEED_WAIT:
1824 case TR_STATUS_DOWNLOAD_WAIT:
1827 return; /* already queued */
1832 case TR_STATUS_CHECK:
1833 case TR_STATUS_CHECK_WAIT:
1834 /* verifying right now... wait until that's done so
1835 * we'll know what completeness to use/announce */
1836 tor->startAfterVerify = true;
1840 case TR_STATUS_STOPPED:
1841 if (!bypass_queue && torrentShouldQueue(tor))
1843 torrentSetQueued(tor, true);
1850 /* don't allow the torrent to be started if the files disappeared */
1851 if (setLocalErrorIfFilesDisappeared(tor))
1856 /* otherwise, start it now... */
1857 tr_sessionLock(tor->session);
1859 /* allow finished torrents to be resumed */
1860 if (tr_torrentIsSeedRatioDone(tor))
1862 tr_logAddTorInfo(tor, "%s", _("Restarted manually -- disabling its seed ratio"));
1863 tr_torrentSetRatioMode(tor, TR_RATIOLIMIT_UNLIMITED);
1866 /* corresponds to the peer_id sent as a tracker request parameter.
1867 * one tracker admin says: "When the same torrent is opened and
1868 * closed and opened again without quitting Transmission ...
1869 * change the peerid. It would help sometimes if a stopped event
1870 * was missed to ensure that we didn't think someone was cheating. */
1871 tr_torrentUnsetPeerId(tor);
1872 tor->isRunning = true;
1873 tr_torrentSetDirty(tor);
1874 tr_runInEventThread(tor->session, torrentStartImpl, tor);
1876 tr_sessionUnlock(tor->session);
1879 void tr_torrentStart(tr_torrent* tor)
1881 if (tr_isTorrent(tor))
1883 torrentStart(tor, false);
1887 void tr_torrentStartNow(tr_torrent* tor)
1889 if (tr_isTorrent(tor))
1891 torrentStart(tor, true);
1899 tr_verify_done_func callback_func;
1900 void* callback_data;
1903 static void onVerifyDoneThreadFunc(void* vdata)
1905 struct verify_data* data = vdata;
1906 tr_torrent* tor = data->tor;
1908 if (tor->isDeleting)
1915 tr_torrentRecheckCompleteness(tor);
1918 if (data->callback_func != NULL)
1920 (*data->callback_func)(tor, data->aborted, data->callback_data);
1923 if (!data->aborted && tor->startAfterVerify)
1925 tor->startAfterVerify = false;
1926 torrentStart(tor, false);
1933 static void onVerifyDone(tr_torrent* tor, bool aborted, void* vdata)
1935 struct verify_data* data = vdata;
1937 TR_ASSERT(data->tor == tor);
1939 if (tor->isDeleting)
1945 data->aborted = aborted;
1946 tr_runInEventThread(tor->session, onVerifyDoneThreadFunc, data);
1949 static void verifyTorrent(void* vdata)
1952 struct verify_data* data = vdata;
1953 tr_torrent* tor = data->tor;
1954 tr_sessionLock(tor->session);
1956 if (tor->isDeleting)
1962 /* if the torrent's already being verified, stop it */
1963 tr_verifyRemove(tor);
1965 startAfter = (tor->isRunning || tor->startAfterVerify) && !tor->isStopping;
1969 tr_torrentStop(tor);
1972 tor->startAfterVerify = startAfter;
1974 if (setLocalErrorIfFilesDisappeared(tor))
1976 tor->startAfterVerify = false;
1980 tr_verifyAdd(tor, onVerifyDone, data);
1984 tr_sessionUnlock(tor->session);
1987 void tr_torrentVerify(tr_torrent* tor, tr_verify_done_func callback_func, void* callback_data)
1989 struct verify_data* data;
1991 data = tr_new(struct verify_data, 1);
1993 data->aborted = false;
1994 data->callback_func = callback_func;
1995 data->callback_data = callback_data;
1996 tr_runInEventThread(tor->session, verifyTorrent, data);
1999 void tr_torrentSave(tr_torrent* tor)
2001 TR_ASSERT(tr_isTorrent(tor));
2005 tor->isDirty = false;
2006 tr_torrentSaveResume(tor);
2010 static void stopTorrent(void* vtor)
2012 tr_torrent* tor = vtor;
2014 TR_ASSERT(tr_isTorrent(tor));
2016 tr_logAddTorInfo(tor, "%s", "Pausing");
2018 tr_torrentLock(tor);
2020 tr_verifyRemove(tor);
2021 tr_peerMgrStopTorrent(tor);
2022 tr_announcerTorrentStopped(tor);
2023 tr_cacheFlushTorrent(tor->session->cache, tor);
2025 tr_fdTorrentClose(tor->session, tor->uniqueId);
2027 if (!tor->isDeleting)
2029 tr_torrentSave(tor);
2032 torrentSetQueued(tor, false);
2034 tr_torrentUnlock(tor);
2036 if (tor->magnetVerify)
2038 tor->magnetVerify = false;
2039 tr_logAddTorInfo(tor, "%s", "Magnet Verify");
2040 refreshCurrentDir(tor);
2041 tr_torrentVerify(tor, NULL, NULL);
2045 void tr_torrentStop(tr_torrent* tor)
2047 TR_ASSERT(tr_isTorrent(tor));
2049 if (tr_isTorrent(tor))
2051 tr_sessionLock(tor->session);
2053 tor->isRunning = false;
2054 tor->isStopping = false;
2055 tr_torrentSetDirty(tor);
2056 tr_runInEventThread(tor->session, stopTorrent, tor);
2058 tr_sessionUnlock(tor->session);
2062 static void closeTorrent(void* vtor)
2064 tr_torrent* tor = vtor;
2066 TR_ASSERT(tr_isTorrent(tor));
2068 tr_variant* d = tr_variantListAddDict(&tor->session->removedTorrents, 2);
2069 tr_variantDictAddInt(d, TR_KEY_id, tor->uniqueId);
2070 tr_variantDictAddInt(d, TR_KEY_date, tr_time());
2072 tr_logAddTorInfo(tor, "%s", _("Removing torrent"));
2074 tor->magnetVerify = false;
2077 if (tor->isDeleting)
2079 tr_metainfoRemoveSaved(tor->session, &tor->info);
2080 tr_torrentRemoveResume(tor);
2083 tor->isRunning = false;
2087 void tr_torrentFree(tr_torrent* tor)
2089 if (tr_isTorrent(tor))
2091 tr_session* session = tor->session;
2093 TR_ASSERT(tr_isSession(session));
2095 tr_sessionLock(session);
2097 tr_torrentClearCompletenessCallback(tor);
2098 tr_runInEventThread(session, closeTorrent, tor);
2100 tr_sessionUnlock(session);
2108 tr_fileFunc deleteFunc;
2111 static void tr_torrentDeleteLocalData(tr_torrent*, tr_fileFunc);
2113 static void removeTorrent(void* vdata)
2115 struct remove_data* data = vdata;
2116 tr_session* session = data->tor->session;
2117 tr_sessionLock(session);
2119 if (data->deleteFlag)
2121 tr_torrentDeleteLocalData(data->tor, data->deleteFunc);
2124 tr_torrentClearCompletenessCallback(data->tor);
2125 closeTorrent(data->tor);
2128 tr_sessionUnlock(session);
2131 void tr_torrentRemove(tr_torrent* tor, bool deleteFlag, tr_fileFunc deleteFunc)
2133 TR_ASSERT(tr_isTorrent(tor));
2135 tor->isDeleting = true;
2137 struct remove_data* data = tr_new0(struct remove_data, 1);
2139 data->deleteFlag = deleteFlag;
2140 data->deleteFunc = deleteFunc;
2141 tr_runInEventThread(tor->session, removeTorrent, data);
2148 static char const* getCompletionString(int type)
2152 case TR_PARTIAL_SEED:
2153 /* Translators: this is a minor point that's safe to skip over, but FYI:
2154 "Complete" and "Done" are specific, different terms in Transmission:
2155 "Complete" means we've downloaded every file in the torrent.
2156 "Done" means we're done downloading the files we wanted, but NOT all
2161 return _("Complete");
2164 return _("Incomplete");
2168 static void fireCompletenessChange(tr_torrent* tor, tr_completeness status, bool wasRunning)
2170 TR_ASSERT(status == TR_LEECH || status == TR_SEED || status == TR_PARTIAL_SEED);
2172 if (tor->completeness_func != NULL)
2174 (*tor->completeness_func)(tor, status, wasRunning, tor->completeness_func_user_data);
2178 void tr_torrentSetCompletenessCallback(tr_torrent* tor, tr_torrent_completeness_func func, void* user_data)
2180 TR_ASSERT(tr_isTorrent(tor));
2182 tor->completeness_func = func;
2183 tor->completeness_func_user_data = user_data;
2186 void tr_torrentClearCompletenessCallback(tr_torrent* torrent)
2188 tr_torrentSetCompletenessCallback(torrent, NULL, NULL);
2191 void tr_torrentSetRatioLimitHitCallback(tr_torrent* tor, tr_torrent_ratio_limit_hit_func func, void* user_data)
2193 TR_ASSERT(tr_isTorrent(tor));
2195 tor->ratio_limit_hit_func = func;
2196 tor->ratio_limit_hit_func_user_data = user_data;
2199 void tr_torrentClearRatioLimitHitCallback(tr_torrent* torrent)
2201 tr_torrentSetRatioLimitHitCallback(torrent, NULL, NULL);
2204 void tr_torrentSetIdleLimitHitCallback(tr_torrent* tor, tr_torrent_idle_limit_hit_func func, void* user_data)
2206 TR_ASSERT(tr_isTorrent(tor));
2208 tor->idle_limit_hit_func = func;
2209 tor->idle_limit_hit_func_user_data = user_data;
2212 void tr_torrentClearIdleLimitHitCallback(tr_torrent* torrent)
2214 tr_torrentSetIdleLimitHitCallback(torrent, NULL, NULL);
2217 static void get_local_time_str(char* const buffer, size_t const buffer_len)
2219 time_t const now = tr_time();
2221 tr_strlcpy(buffer, ctime(&now), buffer_len);
2223 char* newline_pos = strchr(buffer, '\n');
2225 /* ctime() includes '\n', but it's better to be safe */
2226 if (newline_pos != NULL)
2228 *newline_pos = '\0';
2232 static void torrentCallScript(tr_torrent const* tor, char const* script)
2234 if (script == NULL || *script == '\0')
2240 get_local_time_str(time_str, TR_N_ELEMENTS(time_str));
2242 char* const torrent_dir = tr_sys_path_native_separators(tr_strdup(tor->currentDir));
2252 tr_strdup_printf("TR_APP_VERSION=%s", SHORT_VERSION_STRING),
2253 tr_strdup_printf("TR_TIME_LOCALTIME=%s", time_str),
2254 tr_strdup_printf("TR_TORRENT_DIR=%s", torrent_dir),
2255 tr_strdup_printf("TR_TORRENT_HASH=%s", tor->info.hashString),
2256 tr_strdup_printf("TR_TORRENT_ID=%d", tr_torrentId(tor)),
2257 tr_strdup_printf("TR_TORRENT_NAME=%s", tr_torrentName(tor)),
2261 tr_logAddTorInfo(tor, "Calling script \"%s\"", script);
2263 tr_error* error = NULL;
2265 if (!tr_spawn_async(cmd, env, TR_IF_WIN32("\\", "/"), &error))
2267 tr_logAddTorErr(tor, "Error executing script \"%s\" (%d): %s", script, error->code, error->message);
2268 tr_error_free(error);
2271 tr_free_ptrv((void* const*)env);
2272 tr_free_ptrv((void* const*)cmd);
2273 tr_free(torrent_dir);
2276 void tr_torrentRecheckCompleteness(tr_torrent* tor)
2278 tr_completeness completeness;
2280 tr_torrentLock(tor);
2282 completeness = tr_cpGetStatus(&tor->completion);
2284 if (completeness != tor->completeness)
2286 bool const recentChange = tor->downloadedCur != 0;
2287 bool const wasLeeching = !tr_torrentIsSeed(tor);
2288 bool const wasRunning = tor->isRunning;
2292 tr_logAddTorInfo(tor, _("State changed from \"%1$s\" to \"%2$s\""), getCompletionString(tor->completeness),
2293 getCompletionString(completeness));
2296 tor->completeness = completeness;
2297 tr_fdTorrentClose(tor->session, tor->uniqueId);
2299 if (tr_torrentIsSeed(tor))
2303 tr_announcerTorrentCompleted(tor);
2304 tor->doneDate = tor->anyDate = tr_time();
2307 if (wasLeeching && wasRunning)
2309 /* clear interested flag on all peers */
2310 tr_peerMgrClearInterest(tor);
2313 if (tor->currentDir == tor->incompleteDir)
2315 tr_torrentSetLocation(tor, tor->downloadDir, true, NULL, NULL);
2319 fireCompletenessChange(tor, completeness, wasRunning);
2321 if (tr_torrentIsSeed(tor) && wasLeeching && wasRunning)
2323 /* if completeness was TR_LEECH, the seed limit check
2324 will have been skipped in bandwidthPulse */
2325 tr_torrentCheckSeedLimit(tor);
2328 tr_torrentSetDirty(tor);
2330 if (tr_torrentIsSeed(tor) && tr_sessionIsTorrentDoneScriptEnabled(tor->session))
2332 tr_torrentSave(tor);
2334 torrentCallScript(tor, tr_sessionGetTorrentDoneScript(tor->session));
2338 tr_torrentUnlock(tor);
2345 static void tr_torrentFireMetadataCompleted(tr_torrent* tor)
2347 TR_ASSERT(tr_isTorrent(tor));
2349 if (tor->metadata_func != NULL)
2351 (*tor->metadata_func)(tor, tor->metadata_func_user_data);
2355 void tr_torrentSetMetadataCallback(tr_torrent* tor, tr_torrent_metadata_func func, void* user_data)
2357 TR_ASSERT(tr_isTorrent(tor));
2359 tor->metadata_func = func;
2360 tor->metadata_func_user_data = user_data;
2367 void tr_torrentInitFilePriority(tr_torrent* tor, tr_file_index_t fileIndex, tr_priority_t priority)
2369 TR_ASSERT(tr_isTorrent(tor));
2370 TR_ASSERT(fileIndex < tor->info.fileCount);
2371 TR_ASSERT(tr_isPriority(priority));
2373 tr_file* file = &tor->info.files[fileIndex];
2375 file->priority = priority;
2377 for (tr_piece_index_t i = file->firstPiece; i <= file->lastPiece; ++i)
2379 tor->info.pieces[i].priority = calculatePiecePriority(tor, i, fileIndex);
2383 void tr_torrentSetFilePriorities(tr_torrent* tor, tr_file_index_t const* files, tr_file_index_t fileCount,
2384 tr_priority_t priority)
2386 TR_ASSERT(tr_isTorrent(tor));
2388 tr_torrentLock(tor);
2390 for (tr_file_index_t i = 0; i < fileCount; ++i)
2392 if (files[i] < tor->info.fileCount)
2394 tr_torrentInitFilePriority(tor, files[i], priority);
2398 tr_torrentSetDirty(tor);
2399 tr_peerMgrRebuildRequests(tor);
2401 tr_torrentUnlock(tor);
2404 tr_priority_t* tr_torrentGetFilePriorities(tr_torrent const* tor)
2406 TR_ASSERT(tr_isTorrent(tor));
2408 tr_priority_t* p = tr_new0(tr_priority_t, tor->info.fileCount);
2410 for (tr_file_index_t i = 0; i < tor->info.fileCount; ++i)
2412 p[i] = tor->info.files[i].priority;
2422 static void setFileDND(tr_torrent* tor, tr_file_index_t fileIndex, int doDownload)
2424 int8_t const dnd = !doDownload;
2425 tr_piece_index_t firstPiece;
2426 int8_t firstPieceDND;
2427 tr_piece_index_t lastPiece;
2428 int8_t lastPieceDND;
2429 tr_file* file = &tor->info.files[fileIndex];
2432 firstPiece = file->firstPiece;
2433 lastPiece = file->lastPiece;
2435 /* can't set the first piece to DND unless
2436 every file using that piece is DND */
2437 firstPieceDND = dnd;
2441 for (tr_file_index_t i = fileIndex - 1; firstPieceDND; --i)
2443 if (tor->info.files[i].lastPiece != firstPiece)
2448 firstPieceDND = tor->info.files[i].dnd;
2457 /* can't set the last piece to DND unless
2458 every file using that piece is DND */
2461 for (tr_file_index_t i = fileIndex + 1; lastPieceDND && i < tor->info.fileCount; ++i)
2463 if (tor->info.files[i].firstPiece != lastPiece)
2468 lastPieceDND = tor->info.files[i].dnd;
2471 if (firstPiece == lastPiece)
2473 tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
2477 tor->info.pieces[firstPiece].dnd = firstPieceDND;
2478 tor->info.pieces[lastPiece].dnd = lastPieceDND;
2480 for (tr_piece_index_t pp = firstPiece + 1; pp < lastPiece; ++pp)
2482 tor->info.pieces[pp].dnd = dnd;
2487 void tr_torrentInitFileDLs(tr_torrent* tor, tr_file_index_t const* files, tr_file_index_t fileCount, bool doDownload)
2490 TR_ASSERT(tr_isTorrent(tor));
2492 tr_torrentLock(tor);
2494 for (tr_file_index_t i = 0; i < fileCount; ++i)
2496 if (files[i] < tor->info.fileCount)
2498 setFileDND(tor, files[i], doDownload);
2502 tr_cpInvalidateDND(&tor->completion);
2504 tr_torrentUnlock(tor);
2507 void tr_torrentSetFileDLs(tr_torrent* tor, tr_file_index_t const* files, tr_file_index_t fileCount, bool doDownload)
2509 TR_ASSERT(tr_isTorrent(tor));
2511 tr_torrentLock(tor);
2513 tr_torrentInitFileDLs(tor, files, fileCount, doDownload);
2514 tr_torrentSetDirty(tor);
2515 tr_torrentRecheckCompleteness(tor);
2516 tr_peerMgrRebuildRequests(tor);
2518 tr_torrentUnlock(tor);
2525 tr_priority_t tr_torrentGetPriority(tr_torrent const* tor)
2527 TR_ASSERT(tr_isTorrent(tor));
2529 return tor->bandwidth.priority;
2532 void tr_torrentSetPriority(tr_torrent* tor, tr_priority_t priority)
2534 TR_ASSERT(tr_isTorrent(tor));
2535 TR_ASSERT(tr_isPriority(priority));
2537 if (tor->bandwidth.priority != priority)
2539 tor->bandwidth.priority = priority;
2541 tr_torrentSetDirty(tor);
2549 void tr_torrentSetPeerLimit(tr_torrent* tor, uint16_t maxConnectedPeers)
2551 TR_ASSERT(tr_isTorrent(tor));
2553 if (tor->maxConnectedPeers != maxConnectedPeers)
2555 tor->maxConnectedPeers = maxConnectedPeers;
2557 tr_torrentSetDirty(tor);
2561 uint16_t tr_torrentGetPeerLimit(tr_torrent const* tor)
2563 TR_ASSERT(tr_isTorrent(tor));
2565 return tor->maxConnectedPeers;
2572 void tr_torrentGetBlockLocation(tr_torrent const* tor, tr_block_index_t block, tr_piece_index_t* piece, uint32_t* offset,
2575 uint64_t pos = block;
2576 pos *= tor->blockSize;
2577 *piece = pos / tor->info.pieceSize;
2578 *offset = pos - *piece * tor->info.pieceSize;
2579 *length = tr_torBlockCountBytes(tor, block);
2582 tr_block_index_t _tr_block(tr_torrent const* tor, tr_piece_index_t index, uint32_t offset)
2584 TR_ASSERT(tr_isTorrent(tor));
2586 tr_block_index_t ret;
2589 ret *= tor->info.pieceSize / tor->blockSize;
2590 ret += offset / tor->blockSize;
2594 bool tr_torrentReqIsValid(tr_torrent const* tor, tr_piece_index_t index, uint32_t offset, uint32_t length)
2596 TR_ASSERT(tr_isTorrent(tor));
2600 if (index >= tor->info.pieceCount)
2604 else if (length < 1)
2608 else if (offset + length > tr_torPieceCountBytes(tor, index))
2612 else if (length > MAX_BLOCK_SIZE)
2616 else if (tr_pieceOffset(tor, index, offset, length) > tor->info.totalSize)
2623 tr_logAddTorDbg(tor, "index %lu offset %lu length %lu err %d\n", (unsigned long)index, (unsigned long)offset,
2624 (unsigned long)length, err);
2630 uint64_t tr_pieceOffset(tr_torrent const* tor, tr_piece_index_t index, uint32_t offset, uint32_t length)
2632 TR_ASSERT(tr_isTorrent(tor));
2636 ret = tor->info.pieceSize;
2643 void tr_torGetFileBlockRange(tr_torrent const* tor, tr_file_index_t const file, tr_block_index_t* first, tr_block_index_t* last)
2645 tr_file const* f = &tor->info.files[file];
2646 uint64_t offset = f->offset;
2648 *first = offset / tor->blockSize;
2656 offset += f->length - 1;
2657 *last = offset / tor->blockSize;
2661 void tr_torGetPieceBlockRange(tr_torrent const* tor, tr_piece_index_t const piece, tr_block_index_t* first,
2662 tr_block_index_t* last)
2664 uint64_t offset = tor->info.pieceSize;
2666 *first = offset / tor->blockSize;
2667 offset += tr_torPieceCountBytes(tor, piece) - 1;
2668 *last = offset / tor->blockSize;
2675 void tr_torrentSetPieceChecked(tr_torrent* tor, tr_piece_index_t pieceIndex)
2677 TR_ASSERT(tr_isTorrent(tor));
2678 TR_ASSERT(pieceIndex < tor->info.pieceCount);
2680 tor->info.pieces[pieceIndex].timeChecked = tr_time();
2683 void tr_torrentSetChecked(tr_torrent* tor, time_t when)
2685 TR_ASSERT(tr_isTorrent(tor));
2687 for (tr_piece_index_t i = 0; i < tor->info.pieceCount; ++i)
2689 tor->info.pieces[i].timeChecked = when;
2693 bool tr_torrentCheckPiece(tr_torrent* tor, tr_piece_index_t pieceIndex)
2695 bool const pass = tr_ioTestPiece(tor, pieceIndex);
2697 tr_deeplog_tor(tor, "[LAZY] tr_torrentCheckPiece tested piece %zu, pass==%d", (size_t)pieceIndex, (int)pass);
2698 tr_torrentSetHasPiece(tor, pieceIndex, pass);
2699 tr_torrentSetPieceChecked(tor, pieceIndex);
2700 tor->anyDate = tr_time();
2701 tr_torrentSetDirty(tor);
2706 time_t tr_torrentGetFileMTime(tr_torrent const* tor, tr_file_index_t i)
2710 if (!tr_fdFileGetCachedMTime(tor->session, tor->uniqueId, i, &mtime))
2712 tr_torrentFindFile2(tor, i, NULL, NULL, &mtime);
2718 bool tr_torrentPieceNeedsCheck(tr_torrent const* tor, tr_piece_index_t p)
2722 tr_info const* inf = tr_torrentInfo(tor);
2724 /* if we've never checked this piece, then it needs to be checked */
2725 if (inf->pieces[p].timeChecked == 0)
2730 /* If we think we've completed one of the files in this piece,
2731 * but it's been modified since we last checked it,
2732 * then it needs to be rechecked */
2733 tr_ioFindFileLocation(tor, p, 0, &f, &unused);
2735 for (tr_file_index_t i = f; i < inf->fileCount && pieceHasFile(p, &inf->files[i]); ++i)
2737 if (tr_cpFileIsComplete(&tor->completion, i))
2739 if (tr_torrentGetFileMTime(tor, i) > inf->pieces[p].timeChecked)
2753 static int compareTrackerByTier(void const* va, void const* vb)
2755 tr_tracker_info const* a = va;
2756 tr_tracker_info const* b = vb;
2759 if (a->tier != b->tier)
2761 return a->tier - b->tier;
2764 /* get the effects of a stable sort by comparing the two elements' addresses */
2768 bool tr_torrentSetAnnounceList(tr_torrent* tor, tr_tracker_info const* trackers_in, int trackerCount)
2770 TR_ASSERT(tr_isTorrent(tor));
2772 tr_torrentLock(tor);
2774 tr_variant metainfo;
2776 tr_tracker_info* trackers;
2778 /* ensure the trackers' tiers are in ascending order */
2779 trackers = tr_memdup(trackers_in, sizeof(tr_tracker_info) * trackerCount);
2780 qsort(trackers, trackerCount, sizeof(tr_tracker_info), compareTrackerByTier);
2782 /* look for bad URLs */
2783 for (int i = 0; ok && i < trackerCount; ++i)
2785 if (!tr_urlIsValidTracker(trackers[i].announce))
2791 /* save to the .torrent file */
2792 if (ok && tr_variantFromFile(&metainfo, TR_VARIANT_FMT_BENC, tor->info.torrent, NULL))
2797 /* remove the old fields */
2798 tr_variantDictRemove(&metainfo, TR_KEY_announce);
2799 tr_variantDictRemove(&metainfo, TR_KEY_announce_list);
2801 /* add the new fields */
2802 if (trackerCount > 0)
2804 tr_variantDictAddStr(&metainfo, TR_KEY_announce, trackers[0].announce);
2807 if (trackerCount > 1)
2810 tr_variant* tier = NULL;
2811 tr_variant* announceList = tr_variantDictAddList(&metainfo, TR_KEY_announce_list, 0);
2813 for (int i = 0; i < trackerCount; ++i)
2815 if (prevTier != trackers[i].tier)
2817 prevTier = trackers[i].tier;
2818 tier = tr_variantListAddList(announceList, 0);
2821 tr_variantListAddStr(tier, trackers[i].announce);
2825 /* try to parse it back again, to make sure it's good */
2826 memset(&tmpInfo, 0, sizeof(tr_info));
2828 if (tr_metainfoParse(tor->session, &metainfo, &tmpInfo, &hasInfo, &tor->infoDictLength))
2830 /* it's good, so keep these new trackers and free the old ones */
2833 swap.trackers = tor->info.trackers;
2834 swap.trackerCount = tor->info.trackerCount;
2835 tor->info.trackers = tmpInfo.trackers;
2836 tor->info.trackerCount = tmpInfo.trackerCount;
2837 tmpInfo.trackers = swap.trackers;
2838 tmpInfo.trackerCount = swap.trackerCount;
2840 tr_metainfoFree(&tmpInfo);
2841 tr_variantToFile(&metainfo, TR_VARIANT_FMT_BENC, tor->info.torrent);
2845 tr_variantFree(&metainfo);
2847 /* if we had a tracker-related error on this torrent,
2848 * and that tracker's been removed,
2849 * then clear the error */
2850 if (tor->error == TR_STAT_TRACKER_WARNING || tor->error == TR_STAT_TRACKER_ERROR)
2854 for (int i = 0; clear && i < trackerCount; ++i)
2856 if (strcmp(trackers[i].announce, tor->errorTracker) == 0)
2864 tr_torrentClearError(tor);
2868 /* tell the announcer to reload this torrent's tracker list */
2869 tr_announcerResetTorrent(tor->session->announcer, tor);
2872 tr_torrentUnlock(tor);
2882 void tr_torrentSetAddedDate(tr_torrent* tor, time_t t)
2884 TR_ASSERT(tr_isTorrent(tor));
2887 tor->anyDate = MAX(tor->anyDate, tor->addedDate);
2890 void tr_torrentSetActivityDate(tr_torrent* tor, time_t t)
2892 TR_ASSERT(tr_isTorrent(tor));
2894 tor->activityDate = t;
2895 tor->anyDate = MAX(tor->anyDate, tor->activityDate);
2898 void tr_torrentSetDoneDate(tr_torrent* tor, time_t t)
2900 TR_ASSERT(tr_isTorrent(tor));
2903 tor->anyDate = MAX(tor->anyDate, tor->doneDate);
2910 uint64_t tr_torrentGetBytesLeftToAllocate(tr_torrent const* tor)
2912 TR_ASSERT(tr_isTorrent(tor));
2914 uint64_t bytesLeft = 0;
2916 for (tr_file_index_t i = 0; i < tor->info.fileCount; ++i)
2918 if (!tor->info.files[i].dnd)
2920 tr_sys_path_info info;
2921 uint64_t const length = tor->info.files[i].length;
2922 char* path = tr_torrentFindFile(tor, i);
2924 bytesLeft += length;
2926 if (path != NULL && tr_sys_path_get_info(path, 0, &info, NULL) && info.type == TR_SYS_PATH_IS_FILE &&
2927 info.size <= length)
2929 bytesLeft -= info.size;
2940 ***** Removing the torrent's local data
2943 static bool isJunkFile(char const* base)
2945 static char const* files[] =
2952 for (size_t i = 0; i < TR_N_ELEMENTS(files); ++i)
2954 if (strcmp(base, files[i]) == 0)
2962 /* check for resource forks. <http://support.apple.com/kb/TA20578> */
2963 if (memcmp(base, "._", 2) == 0)
2973 static void removeEmptyFoldersAndJunkFiles(char const* folder)
2977 if ((odir = tr_sys_dir_open(folder, NULL)) != TR_BAD_SYS_DIR)
2981 while ((name = tr_sys_dir_read_name(odir, NULL)) != NULL)
2983 if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0)
2985 tr_sys_path_info info;
2986 char* filename = tr_buildPath(folder, name, NULL);
2988 if (tr_sys_path_get_info(filename, 0, &info, NULL) && info.type == TR_SYS_PATH_IS_DIRECTORY)
2990 removeEmptyFoldersAndJunkFiles(filename);
2992 else if (isJunkFile(name))
2994 tr_sys_path_remove(filename, NULL);
3001 tr_sys_path_remove(folder, NULL);
3002 tr_sys_dir_close(odir, NULL);
3007 * This convoluted code does something (seemingly) simple:
3008 * remove the torrent's local files.
3010 * Fun complications:
3011 * 1. Try to preserve the directory hierarchy in the recycle bin.
3012 * 2. If there are nontorrent files, don't delete them...
3013 * 3. ...unless the other files are "junk", such as .DS_Store
3015 static void deleteLocalData(tr_torrent* tor, tr_fileFunc func)
3019 char* tmpdir = NULL;
3020 tr_ptrArray files = TR_PTR_ARRAY_INIT;
3021 tr_ptrArray folders = TR_PTR_ARRAY_INIT;
3022 PtrArrayCompareFunc vstrcmp = (PtrArrayCompareFunc)strcmp;
3023 char const* const top = tor->currentDir;
3025 /* don't try to delete local data if the directory's gone missing */
3026 if (!tr_sys_path_exists(top, NULL))
3031 /* if it's a magnet link, there's nothing to move... */
3032 if (!tr_torrentHasMetadata(tor))
3038 **** Move the local data to a new tmpdir
3041 base = tr_strdup_printf("%s__XXXXXX", tr_torrentName(tor));
3042 tmpdir = tr_buildPath(top, base, NULL);
3043 tr_sys_dir_create_temp(tmpdir, NULL);
3046 for (tr_file_index_t f = 0; f < tor->info.fileCount; ++f)
3050 /* try to find the file, looking in the partial and download dirs */
3051 filename = tr_buildPath(top, tor->info.files[f].name, NULL);
3053 if (!tr_sys_path_exists(filename, NULL))
3055 char* partial = tr_torrentBuildPartial(tor, f);
3057 filename = tr_buildPath(top, partial, NULL);
3060 if (!tr_sys_path_exists(filename, NULL))
3067 /* if we found the file, move it */
3068 if (filename != NULL)
3070 char* target = tr_buildPath(tmpdir, tor->info.files[f].name, NULL);
3071 tr_moveFile(filename, target, NULL);
3072 tr_ptrArrayAppend(&files, target);
3080 **** Try deleting the top-level files & folders to preserve
3081 **** the directory hierarchy in the recycle bin.
3082 **** If case that fails -- for example, rmdir () doesn't
3083 **** delete nonempty folders -- go from the bottom up too.
3086 /* try deleting the local data's top-level files & folders */
3087 if ((odir = tr_sys_dir_open(tmpdir, NULL)) != TR_BAD_SYS_DIR)
3091 while ((name = tr_sys_dir_read_name(odir, NULL)) != NULL)
3093 if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0)
3095 char* file = tr_buildPath(tmpdir, name, NULL);
3096 (*func)(file, NULL);
3101 tr_sys_dir_close(odir, NULL);
3104 /* go from the bottom up */
3105 for (int i = 0, n = tr_ptrArraySize(&files); i < n; ++i)
3107 char* walk = tr_strdup(tr_ptrArrayNth(&files, i));
3109 while (tr_sys_path_exists(walk, NULL) && !tr_sys_path_is_same(tmpdir, walk, NULL))
3111 char* tmp = tr_sys_path_dirname(walk, NULL);
3112 (*func)(walk, NULL);
3121 **** The local data has been removed.
3122 **** What's left in top are empty folders, junk, and user-generated files.
3123 **** Remove the first two categories and leave the third.
3126 /* build a list of 'top's child directories that belong to this torrent */
3127 for (tr_file_index_t f = 0; f < tor->info.fileCount; ++f)
3132 /* get the directory that this file goes in... */
3133 filename = tr_buildPath(top, tor->info.files[f].name, NULL);
3134 dir = tr_sys_path_dirname(filename, NULL);
3142 /* walk up the directory tree until we reach 'top' */
3143 if (!tr_sys_path_is_same(top, dir, NULL) && strcmp(top, dir) != 0)
3147 char* parent = tr_sys_path_dirname(dir, NULL);
3149 if (tr_sys_path_is_same(top, parent, NULL) || strcmp(top, parent) == 0)
3151 if (tr_ptrArrayFindSorted(&folders, dir, vstrcmp) == NULL)
3153 tr_ptrArrayInsertSorted(&folders, tr_strdup(dir), vstrcmp);
3160 /* walk upwards to parent */
3169 for (int i = 0, n = tr_ptrArraySize(&folders); i < n; ++i)
3171 removeEmptyFoldersAndJunkFiles(tr_ptrArrayNth(&folders, i));
3175 tr_sys_path_remove(tmpdir, NULL);
3177 tr_ptrArrayDestruct(&folders, tr_free);
3178 tr_ptrArrayDestruct(&files, tr_free);
3181 static void tr_torrentDeleteLocalData(tr_torrent* tor, tr_fileFunc func)
3183 TR_ASSERT(tr_isTorrent(tor));
3187 func = tr_sys_path_remove;
3190 /* close all the files because we're about to delete them */
3191 tr_cacheFlushTorrent(tor->session->cache, tor);
3192 tr_fdTorrentClose(tor->session, tor->uniqueId);
3194 deleteLocalData(tor, func);
3203 bool move_from_old_location;
3204 int volatile* setme_state;
3205 double volatile* setme_progress;
3210 static void setLocation(void* vdata)
3212 struct LocationData* data = vdata;
3213 tr_torrent* tor = data->tor;
3215 TR_ASSERT(tr_isTorrent(tor));
3217 tr_torrentLock(tor);
3220 bool const do_move = data->move_from_old_location;
3221 char const* location = data->location;
3222 double bytesHandled = 0;
3224 tr_logAddDebug("Moving \"%s\" location from currentDir \"%s\" to \"%s\"", tr_torrentName(tor), tor->currentDir, location);
3226 tr_sys_dir_create(location, TR_SYS_DIR_CREATE_PARENTS, 0777, NULL);
3228 if (!tr_sys_path_is_same(location, tor->currentDir, NULL))
3230 /* bad idea to move files while they're being verified... */
3231 tr_verifyRemove(tor);
3233 /* try to move the files.
3234 * FIXME: there are still all kinds of nasty cases, like what
3235 * if the target directory runs out of space halfway through... */
3236 for (tr_file_index_t i = 0; !err && i < tor->info.fileCount; ++i)
3239 char const* oldbase;
3240 tr_file const* f = &tor->info.files[i];
3242 if (tr_torrentFindFile2(tor, i, &oldbase, &sub, NULL))
3244 char* oldpath = tr_buildPath(oldbase, sub, NULL);
3245 char* newpath = tr_buildPath(location, sub, NULL);
3247 tr_logAddDebug("Found file #%d: %s", (int)i, oldpath);
3249 if (do_move && !tr_sys_path_is_same(oldpath, newpath, NULL))
3251 tr_error* error = NULL;
3253 tr_logAddTorInfo(tor, "moving \"%s\" to \"%s\"", oldpath, newpath);
3255 if (!tr_moveFile(oldpath, newpath, &error))
3258 tr_logAddTorErr(tor, "error moving \"%s\" to \"%s\": %s", oldpath, newpath, error->message);
3259 tr_error_free(error);
3268 if (data->setme_progress != NULL)
3270 bytesHandled += f->length;
3271 *data->setme_progress = bytesHandled / tor->info.totalSize;
3275 if (!err && do_move)
3277 /* blow away the leftover subdirectories in the old location */
3278 tr_torrentDeleteLocalData(tor, tr_sys_path_remove);
3284 /* set the new location and reverify */
3285 tr_torrentSetDownloadDir(tor, location);
3289 tr_free(tor->incompleteDir);
3290 tor->incompleteDir = NULL;
3291 tor->currentDir = tor->downloadDir;
3295 if (data->setme_state != NULL)
3297 *data->setme_state = err ? TR_LOC_ERROR : TR_LOC_DONE;
3301 tr_torrentUnlock(tor);
3302 tr_free(data->location);
3306 void tr_torrentSetLocation(tr_torrent* tor, char const* location, bool move_from_old_location, double volatile* setme_progress,
3307 int volatile* setme_state)
3309 TR_ASSERT(tr_isTorrent(tor));
3311 if (setme_state != NULL)
3313 *setme_state = TR_LOC_MOVING;
3316 if (setme_progress != NULL)
3318 *setme_progress = 0;
3321 /* run this in the libtransmission thread */
3322 struct LocationData* data = tr_new(struct LocationData, 1);
3324 data->location = tr_strdup(location);
3325 data->move_from_old_location = move_from_old_location;
3326 data->setme_state = setme_state;
3327 data->setme_progress = setme_progress;
3328 tr_runInEventThread(tor->session, setLocation, data);
3335 static void tr_torrentFileCompleted(tr_torrent* tor, tr_file_index_t fileIndex)
3339 tr_info const* inf = &tor->info;
3340 tr_file const* f = &inf->files[fileIndex];
3341 time_t const now = tr_time();
3343 /* close the file so that we can reopen in read-only mode as needed */
3344 tr_cacheFlushFile(tor->session->cache, tor, fileIndex);
3345 tr_fdFileClose(tor->session, tor, fileIndex);
3347 /* now that the file is complete and closed, we can start watching its
3348 * mtime timestamp for changes to know if we need to reverify pieces */
3349 for (tr_piece_index_t i = f->firstPiece; i <= f->lastPiece; ++i)
3351 inf->pieces[i].timeChecked = now;
3354 /* if the torrent's current filename isn't the same as the one in the
3355 * metadata -- for example, if it had the ".part" suffix appended to
3356 * it until now -- then rename it to match the one in the metadata */
3357 if (tr_torrentFindFile2(tor, fileIndex, &base, &sub, NULL))
3359 if (strcmp(sub, f->name) != 0)
3361 char* oldpath = tr_buildPath(base, sub, NULL);
3362 char* newpath = tr_buildPath(base, f->name, NULL);
3363 tr_error* error = NULL;
3365 if (!tr_sys_path_rename(oldpath, newpath, &error))
3367 tr_logAddTorErr(tor, "Error moving \"%s\" to \"%s\": %s", oldpath, newpath, error->message);
3368 tr_error_free(error);
3379 static void tr_torrentPieceCompleted(tr_torrent* tor, tr_piece_index_t pieceIndex)
3381 tr_peerMgrPieceCompleted(tor, pieceIndex);
3383 /* if this piece completes any file, invoke the fileCompleted func for it */
3384 for (tr_file_index_t i = 0; i < tor->info.fileCount; ++i)
3386 tr_file const* file = &tor->info.files[i];
3388 if (file->firstPiece <= pieceIndex && pieceIndex <= file->lastPiece)
3390 if (tr_cpFileIsComplete(&tor->completion, i))
3392 tr_torrentFileCompleted(tor, i);
3398 void tr_torrentGotBlock(tr_torrent* tor, tr_block_index_t block)
3400 TR_ASSERT(tr_isTorrent(tor));
3401 TR_ASSERT(tr_amInEventThread(tor->session));
3403 bool const block_is_new = !tr_torrentBlockIsComplete(tor, block);
3409 tr_cpBlockAdd(&tor->completion, block);
3410 tr_torrentSetDirty(tor);
3412 p = tr_torBlockPiece(tor, block);
3414 if (tr_torrentPieceIsComplete(tor, p))
3416 tr_logAddTorDbg(tor, "[LAZY] checking just-completed piece %zu", (size_t)p);
3418 if (tr_torrentCheckPiece(tor, p))
3420 tr_torrentPieceCompleted(tor, p);
3424 uint32_t const n = tr_torPieceCountBytes(tor, p);
3425 tr_logAddTorErr(tor, _("Piece %" PRIu32 ", which was just downloaded, failed its checksum test"), p);
3426 tor->corruptCur += n;
3427 tor->downloadedCur -= MIN(tor->downloadedCur, n);
3428 tr_peerMgrGotBadPiece(tor, p);
3434 uint32_t const n = tr_torBlockCountBytes(tor, block);
3435 tor->downloadedCur -= MIN(tor->downloadedCur, n);
3436 tr_logAddTorDbg(tor, "we have this block already...");
3444 bool tr_torrentFindFile2(tr_torrent const* tor, tr_file_index_t fileNum, char const** base, char** subpath, time_t* mtime)
3446 TR_ASSERT(tr_isTorrent(tor));
3447 TR_ASSERT(fileNum < tor->info.fileCount);
3450 tr_file const* file;
3451 char const* b = NULL;
3452 char const* s = NULL;
3453 tr_sys_path_info file_info;
3455 file = &tor->info.files[fileNum];
3457 /* look in the download dir... */
3460 char* filename = tr_buildPath(tor->downloadDir, file->name, NULL);
3462 if (tr_sys_path_get_info(filename, 0, &file_info, NULL))
3464 b = tor->downloadDir;
3471 /* look in the incomplete dir... */
3472 if (b == NULL && tor->incompleteDir != NULL)
3474 char* filename = tr_buildPath(tor->incompleteDir, file->name, NULL);
3476 if (tr_sys_path_get_info(filename, 0, &file_info, NULL))
3478 b = tor->incompleteDir;
3487 part = tr_torrentBuildPartial(tor, fileNum);
3490 /* look for a .part file in the incomplete dir... */
3491 if (b == NULL && tor->incompleteDir != NULL)
3493 char* filename = tr_buildPath(tor->incompleteDir, part, NULL);
3495 if (tr_sys_path_get_info(filename, 0, &file_info, NULL))
3497 b = tor->incompleteDir;
3504 /* look for a .part file in the download dir... */
3507 char* filename = tr_buildPath(tor->downloadDir, part, NULL);
3509 if (tr_sys_path_get_info(filename, 0, &file_info, NULL))
3511 b = tor->downloadDir;
3518 /* return the results */
3524 if (subpath != NULL)
3526 *subpath = tr_strdup(s);
3531 *mtime = file_info.last_modified_at;
3539 char* tr_torrentFindFile(tr_torrent const* tor, tr_file_index_t fileNum)
3545 if (tr_torrentFindFile2(tor, fileNum, &base, &subpath, NULL))
3547 ret = tr_buildPath(base, subpath, NULL);
3554 /* Decide whether we should be looking for files in downloadDir or incompleteDir. */
3555 static void refreshCurrentDir(tr_torrent* tor)
3557 char const* dir = NULL;
3559 if (tor->incompleteDir == NULL)
3561 dir = tor->downloadDir;
3563 else if (!tr_torrentHasMetadata(tor)) /* no files to find */
3565 dir = tor->incompleteDir;
3567 else if (!tr_torrentFindFile2(tor, 0, &dir, NULL, NULL))
3569 dir = tor->incompleteDir;
3572 TR_ASSERT(dir != NULL);
3573 TR_ASSERT(dir == tor->downloadDir || dir == tor->incompleteDir);
3575 tor->currentDir = dir;
3578 char* tr_torrentBuildPartial(tr_torrent const* tor, tr_file_index_t fileNum)
3580 return tr_strdup_printf("%s.part", tor->info.files[fileNum].name);
3587 static int compareTorrentByQueuePosition(void const* va, void const* vb)
3589 tr_torrent const* a = *(tr_torrent const* const*)va;
3590 tr_torrent const* b = *(tr_torrent const* const*)vb;
3592 return a->queuePosition - b->queuePosition;
3595 #ifdef TR_ENABLE_ASSERTS
3597 static bool queueIsSequenced(tr_session* session)
3601 tr_torrent** torrents;
3604 torrents = tr_sessionGetTorrents(session, &n);
3605 qsort(torrents, n, sizeof(tr_torrent*), compareTorrentByQueuePosition);
3609 fprintf(stderr, "%s", "queue: ");
3611 for (int i = 0; i < n; ++i)
3613 fprintf(stderr, "%d ", tmp[i]->queuePosition);
3616 fputc('\n', stderr);
3621 is_sequenced = true;
3623 for (int i = 0; is_sequenced && i < n; ++i)
3625 is_sequenced = torrents[i]->queuePosition == i;
3629 return is_sequenced;
3634 int tr_torrentGetQueuePosition(tr_torrent const* tor)
3636 return tor->queuePosition;
3639 void tr_torrentSetQueuePosition(tr_torrent* tor, int pos)
3643 int const old_pos = tor->queuePosition;
3644 time_t const now = tr_time();
3651 tor->queuePosition = -1;
3655 while ((walk = tr_torrentNext(tor->session, walk)) != NULL)
3659 if (old_pos <= walk->queuePosition && walk->queuePosition <= pos)
3661 walk->queuePosition--;
3662 walk->anyDate = now;
3668 if (pos <= walk->queuePosition && walk->queuePosition < old_pos)
3670 walk->queuePosition++;
3671 walk->anyDate = now;
3675 if (back < walk->queuePosition)
3677 back = walk->queuePosition;
3681 tor->queuePosition = MIN(pos, back + 1);
3684 TR_ASSERT(queueIsSequenced(tor->session));
3687 void tr_torrentsQueueMoveTop(tr_torrent** torrents_in, int n)
3689 tr_torrent** torrents = tr_memdup(torrents_in, sizeof(tr_torrent*) * n);
3690 qsort(torrents, n, sizeof(tr_torrent*), compareTorrentByQueuePosition);
3692 for (int i = n - 1; i >= 0; --i)
3694 tr_torrentSetQueuePosition(torrents[i], 0);
3700 void tr_torrentsQueueMoveUp(tr_torrent** torrents_in, int n)
3702 tr_torrent** torrents;
3704 torrents = tr_memdup(torrents_in, sizeof(tr_torrent*) * n);
3705 qsort(torrents, n, sizeof(tr_torrent*), compareTorrentByQueuePosition);
3707 for (int i = 0; i < n; ++i)
3709 tr_torrentSetQueuePosition(torrents[i], torrents[i]->queuePosition - 1);
3715 void tr_torrentsQueueMoveDown(tr_torrent** torrents_in, int n)
3717 tr_torrent** torrents;
3719 torrents = tr_memdup(torrents_in, sizeof(tr_torrent*) * n);
3720 qsort(torrents, n, sizeof(tr_torrent*), compareTorrentByQueuePosition);
3722 for (int i = n - 1; i >= 0; --i)
3724 tr_torrentSetQueuePosition(torrents[i], torrents[i]->queuePosition + 1);
3730 void tr_torrentsQueueMoveBottom(tr_torrent** torrents_in, int n)
3732 tr_torrent** torrents;
3734 torrents = tr_memdup(torrents_in, sizeof(tr_torrent*) * n);
3735 qsort(torrents, n, sizeof(tr_torrent*), compareTorrentByQueuePosition);
3737 for (int i = 0; i < n; ++i)
3739 tr_torrentSetQueuePosition(torrents[i], INT_MAX);
3745 static void torrentSetQueued(tr_torrent* tor, bool queued)
3747 TR_ASSERT(tr_isTorrent(tor));
3749 if (tr_torrentIsQueued(tor) != queued)
3751 tor->isQueued = queued;
3752 tor->anyDate = tr_time();
3753 tr_torrentSetDirty(tor);
3757 void tr_torrentSetQueueStartCallback(tr_torrent* torrent, void (* callback)(tr_torrent*, void*), void* user_data)
3759 torrent->queue_started_callback = callback;
3760 torrent->queue_started_user_data = user_data;
3769 static bool renameArgsAreValid(char const* oldpath, char const* newname)
3771 return oldpath != NULL && *oldpath != '\0' && newname != NULL && *newname != '\0' && strcmp(newname, ".") != 0 &&
3772 strcmp(newname, "..") != 0 && strchr(newname, TR_PATH_DELIMITER) == NULL;
3775 static tr_file_index_t* renameFindAffectedFiles(tr_torrent* tor, char const* oldpath, size_t* setme_n)
3779 tr_file_index_t* indices = tr_new0(tr_file_index_t, tor->info.fileCount);
3782 oldpath_len = strlen(oldpath);
3784 for (tr_file_index_t i = 0; i < tor->info.fileCount; ++i)
3786 char const* name = tor->info.files[i].name;
3787 size_t const len = strlen(name);
3789 if ((len == oldpath_len || (len > oldpath_len && name[oldpath_len] == '/')) && memcmp(oldpath, name, oldpath_len) == 0)
3799 static int renamePath(tr_torrent* tor, char const* oldpath, char const* newname)
3805 if (!tr_torrentIsSeed(tor) && tor->incompleteDir != NULL)
3807 base = tor->incompleteDir;
3811 base = tor->downloadDir;
3814 src = tr_buildPath(base, oldpath, NULL);
3816 if (!tr_sys_path_exists(src, NULL)) /* check for it as a partial */
3818 char* tmp = tr_strdup_printf("%s.part", src);
3823 if (tr_sys_path_exists(src, NULL))
3827 char* parent = tr_sys_path_dirname(src, NULL);
3830 if (tr_str_has_suffix(src, ".part"))
3832 tgt = tr_strdup_printf("%s" TR_PATH_DELIMITER_STR "%s.part", parent, newname);
3836 tgt = tr_buildPath(parent, newname, NULL);
3840 tgt_exists = tr_sys_path_exists(tgt, NULL);
3845 tr_error* error = NULL;
3849 if (!tr_sys_path_rename(src, tgt, &error))
3852 tr_error_free(error);
3867 static void renameTorrentFileString(tr_torrent* tor, char const* oldpath, char const* newname, tr_file_index_t fileIndex)
3870 tr_file* file = &tor->info.files[fileIndex];
3871 size_t const oldpath_len = strlen(oldpath);
3873 if (strchr(oldpath, TR_PATH_DELIMITER) == NULL)
3875 if (oldpath_len >= strlen(file->name))
3877 name = tr_buildPath(newname, NULL);
3881 name = tr_buildPath(newname, file->name + oldpath_len + 1, NULL);
3886 char* tmp = tr_sys_path_dirname(oldpath, NULL);
3893 if (oldpath_len >= strlen(file->name))
3895 name = tr_buildPath(tmp, newname, NULL);
3899 name = tr_buildPath(tmp, newname, file->name + oldpath_len + 1, NULL);
3905 if (strcmp(file->name, name) == 0)
3911 tr_free(file->name);
3913 file->is_renamed = true;
3922 tr_torrent_rename_done_func callback;
3923 void* callback_user_data;
3926 static void torrentRenamePath(void* vdata)
3928 struct rename_data* data = vdata;
3929 tr_torrent* const tor = data->tor;
3931 TR_ASSERT(tr_isTorrent(tor));
3938 char const* const oldpath = data->oldpath;
3939 char const* const newname = data->newname;
3941 if (!renameArgsAreValid(oldpath, newname))
3948 tr_file_index_t* file_indices;
3950 file_indices = renameFindAffectedFiles(tor, oldpath, &n);
3958 error = renamePath(tor, oldpath, newname);
3962 /* update tr_info.files */
3963 for (size_t i = 0; i < n; ++i)
3965 renameTorrentFileString(tor, oldpath, newname, file_indices[i]);
3968 /* update tr_info.name if user changed the toplevel */
3969 if (n == tor->info.fileCount && strchr(oldpath, '/') == NULL)
3971 tr_free(tor->info.name);
3972 tor->info.name = tr_strdup(newname);
3975 tr_torrentSetDirty(tor);
3979 tr_free(file_indices);
3986 tor->anyDate = tr_time();
3989 if (data->callback != NULL)
3991 (*data->callback)(tor, data->oldpath, data->newname, error, data->callback_user_data);
3995 tr_free(data->oldpath);
3996 tr_free(data->newname);
4000 void tr_torrentRenamePath(tr_torrent* tor, char const* oldpath, char const* newname, tr_torrent_rename_done_func callback,
4001 void* callback_user_data)
4003 struct rename_data* data;
4005 data = tr_new0(struct rename_data, 1);
4007 data->oldpath = tr_strdup(oldpath);
4008 data->newname = tr_strdup(newname);
4009 data->callback = callback;
4010 data->callback_user_data = callback_user_data;
4012 tr_runInEventThread(tor->session, torrentRenamePath, data);