Transmission: update to 2.76
[tomato.git] / release / src / router / transmission / libtransmission / torrent.c
blob8ecb7dbd02741aced18e0b8cb097368955940bfe
1 /*
2 * This file Copyright (C) Mnemosyne LLC
4 * This file is licensed by the GPL version 2. Works owned by the
5 * Transmission project are granted a special exemption to clause 2 (b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
10 * $Id: torrent.c 13769 2013-01-05 17:46:12Z jordan $
13 #include <signal.h> /* signal () */
14 #include <sys/types.h> /* stat */
15 #include <sys/stat.h> /* stat */
16 #ifndef WIN32
17 #include <sys/wait.h> /* wait () */
18 #else
19 #include <process.h>
20 #define waitpid(pid, status, options) _cwait (status, pid, WAIT_CHILD)
21 #endif
22 #include <unistd.h> /* stat */
23 #include <dirent.h>
25 #include <assert.h>
26 #include <math.h>
27 #include <stdarg.h>
28 #include <string.h> /* memcmp */
29 #include <stdlib.h> /* qsort */
30 #include <stdio.h> /* remove () */
32 #include <event2/util.h> /* evutil_vsnprintf () */
34 #include "transmission.h"
35 #include "announcer.h"
36 #include "bandwidth.h"
37 #include "bencode.h"
38 #include "cache.h"
39 #include "completion.h"
40 #include "crypto.h" /* for tr_sha1 */
41 #include "resume.h"
42 #include "fdlimit.h" /* tr_fdTorrentClose */
43 #include "inout.h" /* tr_ioTestPiece () */
44 #include "magnet.h"
45 #include "metainfo.h"
46 #include "peer-common.h" /* MAX_BLOCK_SIZE */
47 #include "peer-mgr.h"
48 #include "platform.h" /* TR_PATH_DELIMITER_STR */
49 #include "ptrarray.h"
50 #include "session.h"
51 #include "torrent.h"
52 #include "torrent-magnet.h"
53 #include "trevent.h" /* tr_runInEventThread () */
54 #include "utils.h"
55 #include "verify.h"
56 #include "version.h"
58 /***
59 ****
60 ***/
62 #define tr_deeplog_tor(tor, ...) \
63 do \
64 { \
65 if (tr_deepLoggingIsActive ()) \
66 tr_deepLog (__FILE__, __LINE__, tr_torrentName (tor), __VA_ARGS__); \
67 } \
68 while (0)
70 /***
71 ****
72 ***/
74 const char *
75 tr_torrentName (const tr_torrent * tor)
77 assert (tr_isTorrent (tor));
79 return tor->info.name;
82 int
83 tr_torrentId (const tr_torrent * tor)
85 return tor->uniqueId;
88 tr_torrent*
89 tr_torrentFindFromId (tr_session * session, int id)
91 tr_torrent * tor = NULL;
93 while ((tor = tr_torrentNext (session, tor)))
94 if (tor->uniqueId == id)
95 return tor;
97 return NULL;
100 tr_torrent*
101 tr_torrentFindFromHashString (tr_session * session, const char * str)
103 tr_torrent * tor = NULL;
105 while ((tor = tr_torrentNext (session, tor)))
106 if (!evutil_ascii_strcasecmp (str, tor->info.hashString))
107 return tor;
109 return NULL;
112 tr_torrent*
113 tr_torrentFindFromHash (tr_session * session, const uint8_t * torrentHash)
115 tr_torrent * tor = NULL;
117 while ((tor = tr_torrentNext (session, tor)))
118 if (*tor->info.hash == *torrentHash)
119 if (!memcmp (tor->info.hash, torrentHash, SHA_DIGEST_LENGTH))
120 return tor;
122 return NULL;
125 tr_torrent*
126 tr_torrentFindFromMagnetLink (tr_session * session, const char * magnet)
128 tr_magnet_info * info;
129 tr_torrent * tor = NULL;
131 if ((info = tr_magnetParse (magnet)))
133 tor = tr_torrentFindFromHash (session, info->hash);
134 tr_magnetFree (info);
137 return tor;
140 tr_torrent*
141 tr_torrentFindFromObfuscatedHash (tr_session * session,
142 const uint8_t * obfuscatedTorrentHash)
144 tr_torrent * tor = NULL;
146 while ((tor = tr_torrentNext (session, tor)))
147 if (!memcmp (tor->obfuscatedHash, obfuscatedTorrentHash,
148 SHA_DIGEST_LENGTH))
149 return tor;
151 return NULL;
154 bool
155 tr_torrentIsPieceTransferAllowed (const tr_torrent * tor,
156 tr_direction direction)
158 unsigned int limit;
159 bool allowed = true;
161 if (tr_torrentUsesSpeedLimit (tor, direction))
162 if (tr_torrentGetSpeedLimit_Bps (tor, direction) <= 0)
163 allowed = false;
165 if (tr_torrentUsesSessionLimits (tor))
166 if (tr_sessionGetActiveSpeedLimit_Bps (tor->session, direction, &limit))
167 if (limit <= 0)
168 allowed = false;
170 return allowed;
173 /***
174 **** PER-TORRENT UL / DL SPEEDS
175 ***/
177 void
178 tr_torrentSetSpeedLimit_Bps (tr_torrent * tor, tr_direction dir, unsigned int Bps)
180 assert (tr_isTorrent (tor));
181 assert (tr_isDirection (dir));
183 if (tr_bandwidthSetDesiredSpeed_Bps (&tor->bandwidth, dir, Bps))
184 tr_torrentSetDirty (tor);
186 void
187 tr_torrentSetSpeedLimit_KBps (tr_torrent * tor, tr_direction dir, unsigned int KBps)
189 tr_torrentSetSpeedLimit_Bps (tor, dir, toSpeedBytes (KBps));
192 unsigned int
193 tr_torrentGetSpeedLimit_Bps (const tr_torrent * tor, tr_direction dir)
195 assert (tr_isTorrent (tor));
196 assert (tr_isDirection (dir));
198 return tr_bandwidthGetDesiredSpeed_Bps (&tor->bandwidth, dir);
200 unsigned int
201 tr_torrentGetSpeedLimit_KBps (const tr_torrent * tor, tr_direction dir)
203 return toSpeedKBps (tr_torrentGetSpeedLimit_Bps (tor, dir));
206 void
207 tr_torrentUseSpeedLimit (tr_torrent * tor, tr_direction dir, bool do_use)
209 assert (tr_isTorrent (tor));
210 assert (tr_isDirection (dir));
212 if (tr_bandwidthSetLimited (&tor->bandwidth, dir, do_use))
213 tr_torrentSetDirty (tor);
216 bool
217 tr_torrentUsesSpeedLimit (const tr_torrent * tor, tr_direction dir)
219 assert (tr_isTorrent (tor));
220 assert (tr_isDirection (dir));
222 return tr_bandwidthIsLimited (&tor->bandwidth, dir);
225 void
226 tr_torrentUseSessionLimits (tr_torrent * tor, bool doUse)
228 bool changed;
230 assert (tr_isTorrent (tor));
232 changed = tr_bandwidthHonorParentLimits (&tor->bandwidth, TR_UP, doUse);
233 changed |= tr_bandwidthHonorParentLimits (&tor->bandwidth, TR_DOWN, doUse);
235 if (changed)
236 tr_torrentSetDirty (tor);
239 bool
240 tr_torrentUsesSessionLimits (const tr_torrent * tor)
242 assert (tr_isTorrent (tor));
244 return tr_bandwidthAreParentLimitsHonored (&tor->bandwidth, TR_UP);
247 /***
248 ****
249 ***/
251 void
252 tr_torrentSetRatioMode (tr_torrent * tor, tr_ratiolimit mode)
254 assert (tr_isTorrent (tor));
255 assert (mode==TR_RATIOLIMIT_GLOBAL || mode==TR_RATIOLIMIT_SINGLE || mode==TR_RATIOLIMIT_UNLIMITED);
257 if (mode != tor->ratioLimitMode)
259 tor->ratioLimitMode = mode;
261 tr_torrentSetDirty (tor);
265 tr_ratiolimit
266 tr_torrentGetRatioMode (const tr_torrent * tor)
268 assert (tr_isTorrent (tor));
270 return tor->ratioLimitMode;
273 void
274 tr_torrentSetRatioLimit (tr_torrent * tor, double desiredRatio)
276 assert (tr_isTorrent (tor));
278 if ((int)(desiredRatio*100.0) != (int)(tor->desiredRatio*100.0))
280 tor->desiredRatio = desiredRatio;
282 tr_torrentSetDirty (tor);
286 double
287 tr_torrentGetRatioLimit (const tr_torrent * tor)
289 assert (tr_isTorrent (tor));
291 return tor->desiredRatio;
294 bool
295 tr_torrentGetSeedRatio (const tr_torrent * tor, double * ratio)
297 bool isLimited;
299 switch (tr_torrentGetRatioMode (tor))
301 case TR_RATIOLIMIT_SINGLE:
302 isLimited = true;
303 if (ratio)
304 *ratio = tr_torrentGetRatioLimit (tor);
305 break;
307 case TR_RATIOLIMIT_GLOBAL:
308 isLimited = tr_sessionIsRatioLimited (tor->session);
309 if (isLimited && ratio)
310 *ratio = tr_sessionGetRatioLimit (tor->session);
311 break;
313 default: /* TR_RATIOLIMIT_UNLIMITED */
314 isLimited = false;
315 break;
318 return isLimited;
321 /* returns true if the seed ratio applies --
322 * it applies if the torrent's a seed AND it has a seed ratio set */
323 static bool
324 tr_torrentGetSeedRatioBytes (tr_torrent * tor,
325 uint64_t * setmeLeft,
326 uint64_t * setmeGoal)
328 double seedRatio;
329 bool seedRatioApplies = false;
331 if (tr_torrentGetSeedRatio (tor, &seedRatio))
333 const uint64_t u = tor->uploadedCur + tor->uploadedPrev;
334 const uint64_t d = tor->downloadedCur + tor->downloadedPrev;
335 const uint64_t baseline = d ? d : tr_cpSizeWhenDone (&tor->completion);
336 const uint64_t goal = baseline * seedRatio;
337 if (setmeLeft) *setmeLeft = goal > u ? goal - u : 0;
338 if (setmeGoal) *setmeGoal = goal;
339 seedRatioApplies = tr_torrentIsSeed (tor);
342 return seedRatioApplies;
345 static bool
346 tr_torrentIsSeedRatioDone (tr_torrent * tor)
348 uint64_t bytesLeft;
349 return tr_torrentGetSeedRatioBytes (tor, &bytesLeft, NULL) && !bytesLeft;
352 /***
353 ****
354 ***/
356 void
357 tr_torrentSetIdleMode (tr_torrent * tor, tr_idlelimit mode)
359 assert (tr_isTorrent (tor));
360 assert (mode==TR_IDLELIMIT_GLOBAL || mode==TR_IDLELIMIT_SINGLE || mode==TR_IDLELIMIT_UNLIMITED);
362 if (mode != tor->idleLimitMode)
364 tor->idleLimitMode = mode;
366 tr_torrentSetDirty (tor);
370 tr_idlelimit
371 tr_torrentGetIdleMode (const tr_torrent * tor)
373 assert (tr_isTorrent (tor));
375 return tor->idleLimitMode;
378 void
379 tr_torrentSetIdleLimit (tr_torrent * tor, uint16_t idleMinutes)
381 assert (tr_isTorrent (tor));
383 if (idleMinutes > 0)
385 tor->idleLimitMinutes = idleMinutes;
387 tr_torrentSetDirty (tor);
391 uint16_t
392 tr_torrentGetIdleLimit (const tr_torrent * tor)
394 assert (tr_isTorrent (tor));
396 return tor->idleLimitMinutes;
399 bool
400 tr_torrentGetSeedIdle (const tr_torrent * tor, uint16_t * idleMinutes)
402 bool isLimited;
404 switch (tr_torrentGetIdleMode (tor))
406 case TR_IDLELIMIT_SINGLE:
407 isLimited = true;
408 if (idleMinutes)
409 *idleMinutes = tr_torrentGetIdleLimit (tor);
410 break;
412 case TR_IDLELIMIT_GLOBAL:
413 isLimited = tr_sessionIsIdleLimited (tor->session);
414 if (isLimited && idleMinutes)
415 *idleMinutes = tr_sessionGetIdleLimit (tor->session);
416 break;
418 default: /* TR_IDLELIMIT_UNLIMITED */
419 isLimited = false;
420 break;
423 return isLimited;
426 static bool
427 tr_torrentIsSeedIdleLimitDone (tr_torrent * tor)
429 uint16_t idleMinutes;
430 return tr_torrentGetSeedIdle (tor, &idleMinutes)
431 && difftime (tr_time (), MAX (tor->startDate, tor->activityDate)) >= idleMinutes * 60u;
434 /***
435 ****
436 ***/
438 void
439 tr_torrentCheckSeedLimit (tr_torrent * tor)
441 assert (tr_isTorrent (tor));
443 if (!tor->isRunning || tor->isStopping || !tr_torrentIsSeed (tor))
444 return;
446 /* if we're seeding and reach our seed ratio limit, stop the torrent */
447 if (tr_torrentIsSeedRatioDone (tor))
449 tr_torinf (tor, "Seed ratio reached; pausing torrent");
451 tor->isStopping = true;
453 /* maybe notify the client */
454 if (tor->ratio_limit_hit_func != NULL)
455 tor->ratio_limit_hit_func (tor, tor->ratio_limit_hit_func_user_data);
457 /* if we're seeding and reach our inactiviy limit, stop the torrent */
458 else if (tr_torrentIsSeedIdleLimitDone (tor))
460 tr_torinf (tor, "Seeding idle limit reached; pausing torrent");
462 tor->isStopping = true;
463 tor->finishedSeedingByIdle = true;
465 /* maybe notify the client */
466 if (tor->idle_limit_hit_func != NULL)
467 tor->idle_limit_hit_func (tor, tor->idle_limit_hit_func_user_data);
471 /***
472 ****
473 ***/
475 void
476 tr_torrentSetLocalError (tr_torrent * tor, const char * fmt, ...)
478 va_list ap;
480 assert (tr_isTorrent (tor));
482 va_start (ap, fmt);
483 tor->error = TR_STAT_LOCAL_ERROR;
484 tor->errorTracker[0] = '\0';
485 evutil_vsnprintf (tor->errorString, sizeof (tor->errorString), fmt, ap);
486 va_end (ap);
488 tr_torerr (tor, "%s", tor->errorString);
490 if (tor->isRunning)
491 tor->isStopping = true;
494 static void
495 tr_torrentClearError (tr_torrent * tor)
497 tor->error = TR_STAT_OK;
498 tor->errorString[0] = '\0';
499 tor->errorTracker[0] = '\0';
502 static void
503 onTrackerResponse (tr_torrent * tor, const tr_tracker_event * event, void * unused UNUSED)
505 switch (event->messageType)
507 case TR_TRACKER_PEERS:
509 size_t i;
510 const int8_t seedProbability = event->seedProbability;
511 const bool allAreSeeds = seedProbability == 100;
513 if (allAreSeeds)
514 tr_tordbg (tor, "Got %zu seeds from tracker", event->pexCount);
515 else
516 tr_tordbg (tor, "Got %zu peers from tracker", event->pexCount);
518 for (i = 0; i < event->pexCount; ++i)
519 tr_peerMgrAddPex (tor, TR_PEER_FROM_TRACKER, &event->pex[i], seedProbability);
521 break;
524 case TR_TRACKER_WARNING:
525 tr_torerr (tor, _("Tracker warning: \"%s\""), event->text);
526 tor->error = TR_STAT_TRACKER_WARNING;
527 tr_strlcpy (tor->errorTracker, event->tracker, sizeof (tor->errorTracker));
528 tr_strlcpy (tor->errorString, event->text, sizeof (tor->errorString));
529 break;
531 case TR_TRACKER_ERROR:
532 tr_torerr (tor, _("Tracker error: \"%s\""), event->text);
533 tor->error = TR_STAT_TRACKER_ERROR;
534 tr_strlcpy (tor->errorTracker, event->tracker, sizeof (tor->errorTracker));
535 tr_strlcpy (tor->errorString, event->text, sizeof (tor->errorString));
536 break;
538 case TR_TRACKER_ERROR_CLEAR:
539 if (tor->error != TR_STAT_LOCAL_ERROR)
540 tr_torrentClearError (tor);
541 break;
545 /***
546 ****
547 **** TORRENT INSTANTIATION
548 ****
549 ***/
551 static tr_piece_index_t
552 getBytePiece (const tr_info * info, uint64_t byteOffset)
554 tr_piece_index_t piece;
556 assert (info);
557 assert (info->pieceSize != 0);
559 piece = byteOffset / info->pieceSize;
561 /* handle 0-byte files at the end of a torrent */
562 if (byteOffset == info->totalSize)
563 piece = info->pieceCount - 1;
565 return piece;
568 static void
569 initFilePieces (tr_info * info,
570 tr_file_index_t fileIndex)
572 tr_file * file;
573 uint64_t firstByte, lastByte;
575 assert (info);
576 assert (fileIndex < info->fileCount);
578 file = &info->files[fileIndex];
579 firstByte = file->offset;
580 lastByte = firstByte + (file->length ? file->length - 1 : 0);
581 file->firstPiece = getBytePiece (info, firstByte);
582 file->lastPiece = getBytePiece (info, lastByte);
585 static int
586 pieceHasFile (tr_piece_index_t piece,
587 const tr_file * file)
589 return (file->firstPiece <= piece) && (piece <= file->lastPiece);
592 static tr_priority_t
593 calculatePiecePriority (const tr_torrent * tor,
594 tr_piece_index_t piece,
595 int fileHint)
597 tr_file_index_t i;
598 tr_priority_t priority = TR_PRI_LOW;
600 /* find the first file that has data in this piece */
601 if (fileHint >= 0) {
602 i = fileHint;
603 while (i > 0 && pieceHasFile (piece, &tor->info.files[i - 1]))
604 --i;
605 } else {
606 for (i = 0; i < tor->info.fileCount; ++i)
607 if (pieceHasFile (piece, &tor->info.files[i]))
608 break;
611 /* the piece's priority is the max of the priorities
612 * of all the files in that piece */
613 for (; i < tor->info.fileCount; ++i)
615 const tr_file * file = &tor->info.files[i];
617 if (!pieceHasFile (piece, file))
618 break;
620 priority = MAX (priority, file->priority);
622 /* when dealing with multimedia files, getting the first and
623 last pieces can sometimes allow you to preview it a bit
624 before it's fully downloaded... */
625 if (file->priority >= TR_PRI_NORMAL)
626 if (file->firstPiece == piece || file->lastPiece == piece)
627 priority = TR_PRI_HIGH;
630 return priority;
633 static void
634 tr_torrentInitFilePieces (tr_torrent * tor)
636 int * firstFiles;
637 tr_file_index_t f;
638 tr_piece_index_t p;
639 uint64_t offset = 0;
640 tr_info * inf = &tor->info;
642 /* assign the file offsets */
643 for (f=0; f<inf->fileCount; ++f) {
644 inf->files[f].offset = offset;
645 offset += inf->files[f].length;
646 initFilePieces (inf, f);
649 /* build the array of first-file hints to give calculatePiecePriority */
650 firstFiles = tr_new (int, inf->pieceCount);
651 for (p=f=0; p<inf->pieceCount; ++p) {
652 while (inf->files[f].lastPiece < p)
653 ++f;
654 firstFiles[p] = f;
657 #if 0
658 /* test to confirm the first-file hints are correct */
659 for (p=0; p<inf->pieceCount; ++p) {
660 f = firstFiles[p];
661 assert (inf->files[f].firstPiece <= p);
662 assert (inf->files[f].lastPiece >= p);
663 if (f > 0)
664 assert (inf->files[f-1].lastPiece < p);
665 for (f=0; f<inf->fileCount; ++f)
666 if (pieceHasFile (p, &inf->files[f]))
667 break;
668 assert ((int)f == firstFiles[p]);
670 #endif
672 for (p=0; p<inf->pieceCount; ++p)
673 inf->pieces[p].priority = calculatePiecePriority (tor, p, firstFiles[p]);
675 tr_free (firstFiles);
678 static void torrentStart (tr_torrent * tor, bool bypass_queue);
681 * Decide on a block size. Constraints:
682 * (1) most clients decline requests over 16 KiB
683 * (2) pieceSize must be a multiple of block size
685 uint32_t
686 tr_getBlockSize (uint32_t pieceSize)
688 uint32_t b = pieceSize;
690 while (b > MAX_BLOCK_SIZE)
691 b /= 2u;
693 if (!b || (pieceSize % b)) /* not cleanly divisible */
694 return 0;
695 return b;
698 static void refreshCurrentDir (tr_torrent * tor);
700 static void
701 torrentInitFromInfo (tr_torrent * tor)
703 uint64_t t;
704 tr_info * info = &tor->info;
706 tor->blockSize = tr_getBlockSize (info->pieceSize);
708 if (info->pieceSize)
709 tor->lastPieceSize = (uint32_t)(info->totalSize % info->pieceSize);
711 if (!tor->lastPieceSize)
712 tor->lastPieceSize = info->pieceSize;
714 if (tor->blockSize)
715 tor->lastBlockSize = info->totalSize % tor->blockSize;
717 if (!tor->lastBlockSize)
718 tor->lastBlockSize = tor->blockSize;
720 tor->blockCount = tor->blockSize
721 ? (info->totalSize + tor->blockSize - 1) / tor->blockSize
722 : 0;
724 tor->blockCountInPiece = tor->blockSize
725 ? info->pieceSize / tor->blockSize
726 : 0;
728 tor->blockCountInLastPiece = tor->blockSize
729 ? (tor->lastPieceSize + tor->blockSize - 1) / tor->blockSize
730 : 0;
732 /* check our work */
733 if (tor->blockSize != 0)
734 assert ((info->pieceSize % tor->blockSize) == 0);
735 t = info->pieceCount - 1;
736 t *= info->pieceSize;
737 t += tor->lastPieceSize;
738 assert (t == info->totalSize);
739 t = tor->blockCount - 1;
740 t *= tor->blockSize;
741 t += tor->lastBlockSize;
742 assert (t == info->totalSize);
743 t = info->pieceCount - 1;
744 t *= tor->blockCountInPiece;
745 t += tor->blockCountInLastPiece;
746 assert (t == (uint64_t)tor->blockCount);
748 tr_cpConstruct (&tor->completion, tor);
750 tr_torrentInitFilePieces (tor);
752 tor->completeness = tr_cpGetStatus (&tor->completion);
755 static void tr_torrentFireMetadataCompleted (tr_torrent * tor);
757 void
758 tr_torrentGotNewInfoDict (tr_torrent * tor)
760 torrentInitFromInfo (tor);
762 tr_peerMgrOnTorrentGotMetainfo (tor);
764 tr_torrentFireMetadataCompleted (tor);
767 static bool
768 hasAnyLocalData (const tr_torrent * tor)
770 tr_file_index_t i;
772 for (i=0; i<tor->info.fileCount; ++i)
773 if (tr_torrentFindFile2 (tor, i, NULL, NULL, NULL))
774 return true;
776 return false;
779 static bool
780 setLocalErrorIfFilesDisappeared (tr_torrent * tor)
782 const bool disappeared = (tr_cpHaveTotal (&tor->completion) > 0) && !hasAnyLocalData (tor);
784 if (disappeared)
786 tr_deeplog_tor (tor, "%s", "[LAZY] uh oh, the files disappeared");
787 tr_torrentSetLocalError (tor, "%s", _("No data found! Ensure your drives are connected or use \"Set Location\". To re-download, remove the torrent and re-add it."));
790 return disappeared;
793 static void
794 torrentInit (tr_torrent * tor, const tr_ctor * ctor)
796 int doStart;
797 uint64_t loaded;
798 const char * dir;
799 bool isNewTorrent;
800 struct stat st;
801 static int nextUniqueId = 1;
802 tr_session * session = tr_ctorGetSession (ctor);
804 assert (session != NULL);
806 tr_sessionLock (session);
808 tor->session = session;
809 tor->uniqueId = nextUniqueId++;
810 tor->magicNumber = TORRENT_MAGIC_NUMBER;
811 tor->queuePosition = session->torrentCount;
813 tr_peerIdInit (tor->peer_id);
815 tr_sha1 (tor->obfuscatedHash, "req2", 4,
816 tor->info.hash, SHA_DIGEST_LENGTH,
817 NULL);
819 if (!tr_ctorGetDownloadDir (ctor, TR_FORCE, &dir) ||
820 !tr_ctorGetDownloadDir (ctor, TR_FALLBACK, &dir))
821 tor->downloadDir = tr_strdup (dir);
823 if (tr_ctorGetIncompleteDir (ctor, &dir))
824 dir = tr_sessionGetIncompleteDir (session);
825 if (tr_sessionIsIncompleteDirEnabled (session))
826 tor->incompleteDir = tr_strdup (dir);
828 tr_bandwidthConstruct (&tor->bandwidth, session, &session->bandwidth);
830 tor->bandwidth.priority = tr_ctorGetBandwidthPriority (ctor);
832 tor->error = TR_STAT_OK;
834 tor->finishedSeedingByIdle = false;
836 tr_peerMgrAddTorrent (session->peerMgr, tor);
838 assert (!tor->downloadedCur);
839 assert (!tor->uploadedCur);
841 tr_torrentSetAddedDate (tor, tr_time ()); /* this is a default value to be
842 overwritten by the resume file */
844 torrentInitFromInfo (tor);
845 loaded = tr_torrentLoadResume (tor, ~0, ctor);
846 tor->completeness = tr_cpGetStatus (&tor->completion);
847 setLocalErrorIfFilesDisappeared (tor);
849 tr_ctorInitTorrentPriorities (ctor, tor);
850 tr_ctorInitTorrentWanted (ctor, tor);
852 refreshCurrentDir (tor);
854 doStart = tor->isRunning;
855 tor->isRunning = 0;
857 if (! (loaded & TR_FR_SPEEDLIMIT))
859 tr_torrentUseSpeedLimit (tor, TR_UP, false);
860 tr_torrentSetSpeedLimit_Bps (tor, TR_UP, tr_sessionGetSpeedLimit_Bps (tor->session, TR_UP));
861 tr_torrentUseSpeedLimit (tor, TR_DOWN, false);
862 tr_torrentSetSpeedLimit_Bps (tor, TR_DOWN, tr_sessionGetSpeedLimit_Bps (tor->session, TR_DOWN));
863 tr_torrentUseSessionLimits (tor, true);
866 if (! (loaded & TR_FR_RATIOLIMIT))
868 tr_torrentSetRatioMode (tor, TR_RATIOLIMIT_GLOBAL);
869 tr_torrentSetRatioLimit (tor, tr_sessionGetRatioLimit (tor->session));
872 if (! (loaded & TR_FR_IDLELIMIT))
874 tr_torrentSetIdleMode (tor, TR_IDLELIMIT_GLOBAL);
875 tr_torrentSetIdleLimit (tor, tr_sessionGetIdleLimit (tor->session));
878 /* add the torrent to tr_session.torrentList */
879 session->torrentCount++;
880 if (session->torrentList == NULL)
881 session->torrentList = tor;
882 else {
883 tr_torrent * it = session->torrentList;
884 while (it->next != NULL)
885 it = it->next;
886 it->next = tor;
889 /* if we don't have a local .torrent file already, assume the torrent is new */
890 isNewTorrent = stat (tor->info.torrent, &st);
892 /* maybe save our own copy of the metainfo */
893 if (tr_ctorGetSave (ctor))
895 const tr_benc * val;
896 if (!tr_ctorGetMetainfo (ctor, &val))
898 const char * path = tor->info.torrent;
899 const int err = tr_bencToFile (val, TR_FMT_BENC, path);
900 if (err)
901 tr_torrentSetLocalError (tor, "Unable to save torrent file: %s", tr_strerror (err));
902 tr_sessionSetTorrentFile (tor->session, tor->info.hashString, path);
906 tor->tiers = tr_announcerAddTorrent (tor, onTrackerResponse, NULL);
908 if (isNewTorrent)
910 tor->startAfterVerify = doStart;
911 tr_torrentVerify (tor);
913 else if (doStart)
915 tr_torrentStart (tor);
918 tr_sessionUnlock (session);
921 static tr_parse_result
922 torrentParseImpl (const tr_ctor * ctor, tr_info * setmeInfo,
923 bool * setmeHasInfo, int * dictLength)
925 int doFree;
926 bool didParse;
927 bool hasInfo = false;
928 tr_info tmp;
929 const tr_benc * metainfo;
930 tr_session * session = tr_ctorGetSession (ctor);
931 tr_parse_result result = TR_PARSE_OK;
933 if (setmeInfo == NULL)
934 setmeInfo = &tmp;
935 memset (setmeInfo, 0, sizeof (tr_info));
937 if (tr_ctorGetMetainfo (ctor, &metainfo))
938 return TR_PARSE_ERR;
940 didParse = tr_metainfoParse (session, metainfo, setmeInfo,
941 &hasInfo, dictLength);
942 doFree = didParse && (setmeInfo == &tmp);
944 if (!didParse)
945 result = TR_PARSE_ERR;
947 if (didParse && hasInfo && !tr_getBlockSize (setmeInfo->pieceSize))
948 result = TR_PARSE_ERR;
950 if (didParse && session && tr_torrentExists (session, setmeInfo->hash))
951 result = TR_PARSE_DUPLICATE;
953 if (doFree)
954 tr_metainfoFree (setmeInfo);
956 if (setmeHasInfo != NULL)
957 *setmeHasInfo = hasInfo;
959 return result;
962 tr_parse_result
963 tr_torrentParse (const tr_ctor * ctor, tr_info * setmeInfo)
965 return torrentParseImpl (ctor, setmeInfo, NULL, NULL);
968 tr_torrent *
969 tr_torrentNew (const tr_ctor * ctor, int * setmeError)
971 int len;
972 bool hasInfo;
973 tr_info tmpInfo;
974 tr_parse_result r;
975 tr_torrent * tor = NULL;
977 assert (ctor != NULL);
978 assert (tr_isSession (tr_ctorGetSession (ctor)));
980 r = torrentParseImpl (ctor, &tmpInfo, &hasInfo, &len);
981 if (r == TR_PARSE_OK)
983 tor = tr_new0 (tr_torrent, 1);
984 tor->info = tmpInfo;
985 if (hasInfo)
986 tor->infoDictLength = len;
987 torrentInit (tor, ctor);
989 else
991 if (r == TR_PARSE_DUPLICATE)
992 tr_metainfoFree (&tmpInfo);
994 if (setmeError)
995 *setmeError = r;
998 return tor;
1005 void
1006 tr_torrentSetDownloadDir (tr_torrent * tor, const char * path)
1008 assert (tr_isTorrent (tor));
1010 if (!path || !tor->downloadDir || strcmp (path, tor->downloadDir))
1012 tr_free (tor->downloadDir);
1013 tor->downloadDir = tr_strdup (path);
1014 tr_torrentSetDirty (tor);
1017 refreshCurrentDir (tor);
1020 const char*
1021 tr_torrentGetDownloadDir (const tr_torrent * tor)
1023 assert (tr_isTorrent (tor));
1025 return tor->downloadDir;
1028 const char *
1029 tr_torrentGetCurrentDir (const tr_torrent * tor)
1031 assert (tr_isTorrent (tor));
1033 return tor->currentDir;
1037 void
1038 tr_torrentChangeMyPort (tr_torrent * tor)
1040 assert (tr_isTorrent (tor));
1042 if (tor->isRunning)
1043 tr_announcerChangeMyPort (tor);
1046 static inline void
1047 tr_torrentManualUpdateImpl (void * vtor)
1049 tr_torrent * tor = vtor;
1051 assert (tr_isTorrent (tor));
1053 if (tor->isRunning)
1054 tr_announcerManualAnnounce (tor);
1057 void
1058 tr_torrentManualUpdate (tr_torrent * tor)
1060 assert (tr_isTorrent (tor));
1062 tr_runInEventThread (tor->session, tr_torrentManualUpdateImpl, tor);
1065 bool
1066 tr_torrentCanManualUpdate (const tr_torrent * tor)
1068 return (tr_isTorrent (tor))
1069 && (tor->isRunning)
1070 && (tr_announcerCanManualAnnounce (tor));
1073 const tr_info *
1074 tr_torrentInfo (const tr_torrent * tor)
1076 return tr_isTorrent (tor) ? &tor->info : NULL;
1079 const tr_stat *
1080 tr_torrentStatCached (tr_torrent * tor)
1082 const time_t now = tr_time ();
1084 return tr_isTorrent (tor) && (now == tor->lastStatTime)
1085 ? &tor->stats
1086 : tr_torrentStat (tor);
1089 void
1090 tr_torrentSetVerifyState (tr_torrent * tor, tr_verify_state state)
1092 assert (tr_isTorrent (tor));
1093 assert (state==TR_VERIFY_NONE || state==TR_VERIFY_WAIT || state==TR_VERIFY_NOW);
1095 tor->verifyState = state;
1096 tor->anyDate = tr_time ();
1099 static tr_torrent_activity
1100 torrentGetActivity (const tr_torrent * tor)
1102 const bool is_seed = tr_torrentIsSeed (tor);
1103 assert (tr_isTorrent (tor));
1105 if (tor->verifyState == TR_VERIFY_NOW)
1106 return TR_STATUS_CHECK;
1108 if (tor->verifyState == TR_VERIFY_WAIT)
1109 return TR_STATUS_CHECK_WAIT;
1111 if (tor->isRunning)
1112 return is_seed ? TR_STATUS_SEED : TR_STATUS_DOWNLOAD;
1114 if (tr_torrentIsQueued (tor)) {
1115 if (is_seed && tr_sessionGetQueueEnabled (tor->session, TR_UP))
1116 return TR_STATUS_SEED_WAIT;
1117 if (!is_seed && tr_sessionGetQueueEnabled (tor->session, TR_DOWN))
1118 return TR_STATUS_DOWNLOAD_WAIT;
1121 return TR_STATUS_STOPPED;
1124 tr_torrent_activity
1125 tr_torrentGetActivity (tr_torrent * tor)
1127 /* FIXME: is this call still needed? */
1128 tr_torrentRecheckCompleteness (tor);
1130 return torrentGetActivity (tor);
1133 static time_t
1134 torrentGetIdleSecs (const tr_torrent * tor)
1136 int idle_secs;
1137 const tr_torrent_activity activity = torrentGetActivity (tor);
1139 if ((activity == TR_STATUS_DOWNLOAD || activity == TR_STATUS_SEED) && tor->startDate != 0)
1140 idle_secs = difftime (tr_time (), MAX (tor->startDate, tor->activityDate));
1141 else
1142 idle_secs = -1;
1144 return idle_secs;
1147 bool
1148 tr_torrentIsStalled (const tr_torrent * tor)
1150 return tr_sessionGetQueueStalledEnabled (tor->session)
1151 && (torrentGetIdleSecs (tor) > (tr_sessionGetQueueStalledMinutes (tor->session) * 60));
1155 static double
1156 getVerifyProgress (const tr_torrent * tor)
1158 double d = 0;
1160 assert (tr_isTorrent (tor));
1162 if (tr_torrentHasMetadata (tor))
1164 tr_piece_index_t i, n;
1165 tr_piece_index_t checked = 0;
1167 for (i=0, n=tor->info.pieceCount; i!=n; ++i)
1168 if (tor->info.pieces[i].timeChecked)
1169 ++checked;
1171 d = checked / (double)tor->info.pieceCount;
1174 return d;
1177 const tr_stat *
1178 tr_torrentStat (tr_torrent * tor)
1180 tr_stat * s;
1181 uint64_t now;
1182 uint64_t seedRatioBytesLeft;
1183 uint64_t seedRatioBytesGoal;
1184 bool seedRatioApplies;
1185 uint16_t seedIdleMinutes;
1187 if (!tor)
1188 return NULL;
1190 assert (tr_isTorrent (tor));
1191 tr_torrentLock (tor);
1193 tor->lastStatTime = tr_time ();
1195 s = &tor->stats;
1196 s->id = tor->uniqueId;
1197 s->activity = tr_torrentGetActivity (tor);
1198 s->error = tor->error;
1199 s->queuePosition = tor->queuePosition;
1200 s->isStalled = tr_torrentIsStalled (tor);
1201 tr_strlcpy (s->errorString, tor->errorString, sizeof (s->errorString));
1203 s->manualAnnounceTime = tr_announcerNextManualAnnounce (tor);
1205 tr_peerMgrTorrentStats (tor,
1206 &s->peersConnected,
1207 &s->webseedsSendingToUs,
1208 &s->peersSendingToUs,
1209 &s->peersGettingFromUs,
1210 s->peersFrom);
1212 now = tr_time_msec ();
1213 s->rawUploadSpeed_KBps = toSpeedKBps (tr_bandwidthGetRawSpeed_Bps (&tor->bandwidth, now, TR_UP));
1214 s->pieceUploadSpeed_KBps = toSpeedKBps (tr_bandwidthGetPieceSpeed_Bps (&tor->bandwidth, now, TR_UP));
1215 s->rawDownloadSpeed_KBps = toSpeedKBps (tr_bandwidthGetRawSpeed_Bps (&tor->bandwidth, now, TR_DOWN));
1216 s->pieceDownloadSpeed_KBps = toSpeedKBps (tr_bandwidthGetPieceSpeed_Bps (&tor->bandwidth, now, TR_DOWN));
1218 s->percentComplete = tr_cpPercentComplete (&tor->completion);
1219 s->metadataPercentComplete = tr_torrentGetMetadataPercent (tor);
1221 s->percentDone = tr_cpPercentDone (&tor->completion);
1222 s->leftUntilDone = tr_cpLeftUntilDone (&tor->completion);
1223 s->sizeWhenDone = tr_cpSizeWhenDone (&tor->completion);
1224 s->recheckProgress = s->activity == TR_STATUS_CHECK ? getVerifyProgress (tor) : 0;
1225 s->activityDate = tor->activityDate;
1226 s->addedDate = tor->addedDate;
1227 s->doneDate = tor->doneDate;
1228 s->startDate = tor->startDate;
1229 s->secondsSeeding = tor->secondsSeeding;
1230 s->secondsDownloading = tor->secondsDownloading;
1231 s->idleSecs = torrentGetIdleSecs (tor);
1233 s->corruptEver = tor->corruptCur + tor->corruptPrev;
1234 s->downloadedEver = tor->downloadedCur + tor->downloadedPrev;
1235 s->uploadedEver = tor->uploadedCur + tor->uploadedPrev;
1236 s->haveValid = tr_cpHaveValid (&tor->completion);
1237 s->haveUnchecked = tr_cpHaveTotal (&tor->completion) - s->haveValid;
1238 s->desiredAvailable = tr_peerMgrGetDesiredAvailable (tor);
1240 s->ratio = tr_getRatio (s->uploadedEver,
1241 s->downloadedEver ? s->downloadedEver : s->haveValid);
1243 seedRatioApplies = tr_torrentGetSeedRatioBytes (tor, &seedRatioBytesLeft,
1244 &seedRatioBytesGoal);
1246 switch (s->activity)
1248 /* etaXLSpeed exists because if we use the piece speed directly,
1249 * brief fluctuations cause the ETA to jump all over the place.
1250 * so, etaXLSpeed is a smoothed-out version of the piece speed
1251 * to dampen the effect of fluctuations */
1253 case TR_STATUS_DOWNLOAD:
1254 if ((tor->etaDLSpeedCalculatedAt + 800) < now) {
1255 tor->etaDLSpeed_KBps = ((tor->etaDLSpeedCalculatedAt + 4000) < now)
1256 ? s->pieceDownloadSpeed_KBps /* if no recent previous speed, no need to smooth */
1257 : ((tor->etaDLSpeed_KBps*4.0) + s->pieceDownloadSpeed_KBps)/5.0; /* smooth across 5 readings */
1258 tor->etaDLSpeedCalculatedAt = now;
1261 if ((s->leftUntilDone > s->desiredAvailable) && (tor->info.webseedCount < 1))
1262 s->eta = TR_ETA_NOT_AVAIL;
1263 else if (tor->etaDLSpeed_KBps < 1)
1264 s->eta = TR_ETA_UNKNOWN;
1265 else
1266 s->eta = s->leftUntilDone / toSpeedBytes (tor->etaDLSpeed_KBps);
1268 s->etaIdle = TR_ETA_NOT_AVAIL;
1269 break;
1271 case TR_STATUS_SEED: {
1272 if (!seedRatioApplies)
1273 s->eta = TR_ETA_NOT_AVAIL;
1274 else {
1275 if ((tor->etaULSpeedCalculatedAt + 800) < now) {
1276 tor->etaULSpeed_KBps = ((tor->etaULSpeedCalculatedAt + 4000) < now)
1277 ? s->pieceUploadSpeed_KBps /* if no recent previous speed, no need to smooth */
1278 : ((tor->etaULSpeed_KBps*4.0) + s->pieceUploadSpeed_KBps)/5.0; /* smooth across 5 readings */
1279 tor->etaULSpeedCalculatedAt = now;
1281 if (tor->etaULSpeed_KBps < 1)
1282 s->eta = TR_ETA_UNKNOWN;
1283 else
1284 s->eta = seedRatioBytesLeft / toSpeedBytes (tor->etaULSpeed_KBps);
1287 if (tor->etaULSpeed_KBps < 1 && tr_torrentGetSeedIdle (tor, &seedIdleMinutes))
1288 s->etaIdle = seedIdleMinutes * 60 - s->idleSecs;
1289 else
1290 s->etaIdle = TR_ETA_NOT_AVAIL;
1291 break;
1294 default:
1295 s->eta = TR_ETA_NOT_AVAIL;
1296 s->etaIdle = TR_ETA_NOT_AVAIL;
1297 break;
1300 /* s->haveValid is here to make sure a torrent isn't marked 'finished'
1301 * when the user hits "uncheck all" prior to starting the torrent... */
1302 s->finished = tor->finishedSeedingByIdle || (seedRatioApplies && !seedRatioBytesLeft && s->haveValid);
1304 if (!seedRatioApplies || s->finished)
1305 s->seedRatioPercentDone = 1;
1306 else if (!seedRatioBytesGoal) /* impossible? safeguard for div by zero */
1307 s->seedRatioPercentDone = 0;
1308 else
1309 s->seedRatioPercentDone = (double)(seedRatioBytesGoal - seedRatioBytesLeft) / seedRatioBytesGoal;
1311 tr_torrentUnlock (tor);
1313 /* test some of the constraints */
1314 assert (s->sizeWhenDone <= tor->info.totalSize);
1315 assert (s->leftUntilDone <= s->sizeWhenDone);
1316 assert (s->desiredAvailable <= s->leftUntilDone);
1317 return s;
1320 /***
1321 ****
1322 ***/
1324 static uint64_t
1325 fileBytesCompleted (const tr_torrent * tor, tr_file_index_t index)
1327 uint64_t total = 0;
1328 const tr_file * f = &tor->info.files[index];
1330 if (f->length)
1332 tr_block_index_t first;
1333 tr_block_index_t last;
1334 tr_torGetFileBlockRange (tor, index, &first, &last);
1336 if (first == last)
1338 if (tr_cpBlockIsComplete (&tor->completion, first))
1339 total = f->length;
1341 else
1343 /* the first block */
1344 if (tr_cpBlockIsComplete (&tor->completion, first))
1345 total += tor->blockSize - (f->offset % tor->blockSize);
1347 /* the middle blocks */
1348 if (first + 1 < last) {
1349 uint64_t u = tr_bitfieldCountRange (&tor->completion.blockBitfield, first+1, last);
1350 u *= tor->blockSize;
1351 total += u;
1354 /* the last block */
1355 if (tr_cpBlockIsComplete (&tor->completion, last))
1356 total += (f->offset + f->length) - ((uint64_t)tor->blockSize * last);
1360 return total;
1363 tr_file_stat *
1364 tr_torrentFiles (const tr_torrent * tor,
1365 tr_file_index_t * fileCount)
1367 tr_file_index_t i;
1368 const tr_file_index_t n = tor->info.fileCount;
1369 tr_file_stat * files = tr_new0 (tr_file_stat, n);
1370 tr_file_stat * walk = files;
1371 const bool isSeed = tor->completeness == TR_SEED;
1373 assert (tr_isTorrent (tor));
1375 for (i=0; i<n; ++i, ++walk) {
1376 const uint64_t b = isSeed ? tor->info.files[i].length : fileBytesCompleted (tor, i);
1377 walk->bytesCompleted = b;
1378 walk->progress = tor->info.files[i].length > 0 ? ((float)b / tor->info.files[i].length) : 1.0f;
1381 if (fileCount)
1382 *fileCount = n;
1384 return files;
1387 void
1388 tr_torrentFilesFree (tr_file_stat * files,
1389 tr_file_index_t fileCount UNUSED)
1391 tr_free (files);
1394 /***
1395 ****
1396 ***/
1398 double*
1399 tr_torrentWebSpeeds_KBps (const tr_torrent * tor)
1401 double * ret = NULL;
1403 if (tr_isTorrent (tor))
1405 tr_torrentLock (tor);
1406 ret = tr_peerMgrWebSpeeds_KBps (tor);
1407 tr_torrentUnlock (tor);
1410 return ret;
1413 tr_peer_stat *
1414 tr_torrentPeers (const tr_torrent * tor, int * peerCount)
1416 tr_peer_stat * ret = NULL;
1418 if (tr_isTorrent (tor))
1420 tr_torrentLock (tor);
1421 ret = tr_peerMgrPeerStats (tor, peerCount);
1422 tr_torrentUnlock (tor);
1425 return ret;
1428 void
1429 tr_torrentPeersFree (tr_peer_stat * peers, int peerCount UNUSED)
1431 tr_free (peers);
1434 tr_tracker_stat *
1435 tr_torrentTrackers (const tr_torrent * torrent, int * setmeTrackerCount)
1437 tr_tracker_stat * ret = NULL;
1439 if (tr_isTorrent (torrent))
1441 tr_torrentLock (torrent);
1442 ret = tr_announcerStats (torrent, setmeTrackerCount);
1443 tr_torrentUnlock (torrent);
1446 return ret;
1449 void
1450 tr_torrentTrackersFree (tr_tracker_stat * trackers, int trackerCount)
1452 tr_announcerStatsFree (trackers, trackerCount);
1455 void
1456 tr_torrentAvailability (const tr_torrent * tor, int8_t * tab, int size)
1458 if (tr_isTorrent (tor) && (tab != NULL) && (size > 0))
1460 tr_torrentLock (tor);
1461 tr_peerMgrTorrentAvailability (tor, tab, size);
1462 tr_torrentUnlock (tor);
1466 void
1467 tr_torrentAmountFinished (const tr_torrent * tor,
1468 float * tab,
1469 int size)
1471 assert (tr_isTorrent (tor));
1473 tr_torrentLock (tor);
1474 tr_cpGetAmountDone (&tor->completion, tab, size);
1475 tr_torrentUnlock (tor);
1478 static void
1479 tr_torrentResetTransferStats (tr_torrent * tor)
1481 tr_torrentLock (tor);
1483 tor->downloadedPrev += tor->downloadedCur;
1484 tor->downloadedCur = 0;
1485 tor->uploadedPrev += tor->uploadedCur;
1486 tor->uploadedCur = 0;
1487 tor->corruptPrev += tor->corruptCur;
1488 tor->corruptCur = 0;
1490 tr_torrentSetDirty (tor);
1492 tr_torrentUnlock (tor);
1495 void
1496 tr_torrentSetHasPiece (tr_torrent * tor,
1497 tr_piece_index_t pieceIndex,
1498 bool has)
1500 assert (tr_isTorrent (tor));
1501 assert (pieceIndex < tor->info.pieceCount);
1503 if (has)
1504 tr_cpPieceAdd (&tor->completion, pieceIndex);
1505 else
1506 tr_cpPieceRem (&tor->completion, pieceIndex);
1509 /***
1510 ****
1511 ***/
1513 #ifndef NDEBUG
1514 static bool queueIsSequenced (tr_session *);
1515 #endif
1517 static void
1518 freeTorrent (tr_torrent * tor)
1520 tr_torrent * t;
1521 tr_session * session = tor->session;
1522 tr_info * inf = &tor->info;
1523 const time_t now = tr_time ();
1525 assert (!tor->isRunning);
1527 tr_sessionLock (session);
1529 tr_peerMgrRemoveTorrent (tor);
1531 tr_announcerRemoveTorrent (session->announcer, tor);
1533 tr_cpDestruct (&tor->completion);
1535 tr_free (tor->downloadDir);
1536 tr_free (tor->incompleteDir);
1538 if (tor == session->torrentList)
1539 session->torrentList = tor->next;
1540 else for (t = session->torrentList; t != NULL; t = t->next) {
1541 if (t->next == tor) {
1542 t->next = tor->next;
1543 break;
1547 /* decrement the torrent count */
1548 assert (session->torrentCount >= 1);
1549 session->torrentCount--;
1551 /* resequence the queue positions */
1552 t = NULL;
1553 while ((t = tr_torrentNext (session, t))) {
1554 if (t->queuePosition > tor->queuePosition) {
1555 t->queuePosition--;
1556 t->anyDate = now;
1559 assert (queueIsSequenced (session));
1561 tr_bandwidthDestruct (&tor->bandwidth);
1563 tr_metainfoFree (inf);
1564 memset (tor, ~0, sizeof (tr_torrent));
1565 tr_free (tor);
1567 tr_sessionUnlock (session);
1571 *** Start/Stop Callback
1574 static void torrentSetQueued (tr_torrent * tor, bool queued);
1576 static void
1577 torrentStartImpl (void * vtor)
1579 time_t now;
1580 tr_torrent * tor = vtor;
1582 assert (tr_isTorrent (tor));
1584 tr_sessionLock (tor->session);
1586 tr_torrentRecheckCompleteness (tor);
1587 torrentSetQueued (tor, false);
1589 now = tr_time ();
1590 tor->isRunning = true;
1591 tor->completeness = tr_cpGetStatus (&tor->completion);
1592 tor->startDate = tor->anyDate = now;
1593 tr_torrentClearError (tor);
1594 tor->finishedSeedingByIdle = false;
1596 tr_torrentResetTransferStats (tor);
1597 tr_announcerTorrentStarted (tor);
1598 tor->dhtAnnounceAt = now + tr_cryptoWeakRandInt (20);
1599 tor->dhtAnnounce6At = now + tr_cryptoWeakRandInt (20);
1600 tor->lpdAnnounceAt = now;
1601 tr_peerMgrStartTorrent (tor);
1603 tr_sessionUnlock (tor->session);
1606 uint64_t
1607 tr_torrentGetCurrentSizeOnDisk (const tr_torrent * tor)
1609 tr_file_index_t i;
1610 uint64_t byte_count = 0;
1611 const tr_file_index_t n = tor->info.fileCount;
1613 for (i=0; i<n; ++i)
1615 struct stat sb;
1616 char * filename = tr_torrentFindFile (tor, i);
1618 sb.st_size = 0;
1619 if (filename && !stat (filename, &sb))
1620 byte_count += sb.st_size;
1622 tr_free (filename);
1625 return byte_count;
1628 static bool
1629 torrentShouldQueue (const tr_torrent * tor)
1631 const tr_direction dir = tr_torrentGetQueueDirection (tor);
1633 return tr_sessionCountQueueFreeSlots (tor->session, dir) == 0;
1636 static void
1637 torrentStart (tr_torrent * tor, bool bypass_queue)
1639 switch (torrentGetActivity (tor))
1641 case TR_STATUS_SEED:
1642 case TR_STATUS_DOWNLOAD:
1643 return; /* already started */
1644 break;
1646 case TR_STATUS_SEED_WAIT:
1647 case TR_STATUS_DOWNLOAD_WAIT:
1648 if (!bypass_queue)
1649 return; /* already queued */
1650 break;
1652 case TR_STATUS_CHECK:
1653 case TR_STATUS_CHECK_WAIT:
1654 /* verifying right now... wait until that's done so
1655 * we'll know what completeness to use/announce */
1656 tor->startAfterVerify = true;
1657 return;
1658 break;
1660 case TR_STATUS_STOPPED:
1661 if (!bypass_queue && torrentShouldQueue (tor)) {
1662 torrentSetQueued (tor, true);
1663 return;
1665 break;
1668 /* don't allow the torrent to be started if the files disappeared */
1669 if (setLocalErrorIfFilesDisappeared (tor))
1670 return;
1672 /* otherwise, start it now... */
1673 tr_sessionLock (tor->session);
1675 /* allow finished torrents to be resumed */
1676 if (tr_torrentIsSeedRatioDone (tor)) {
1677 tr_torinf (tor, _("Restarted manually -- disabling its seed ratio"));
1678 tr_torrentSetRatioMode (tor, TR_RATIOLIMIT_UNLIMITED);
1681 /* corresponds to the peer_id sent as a tracker request parameter.
1682 * one tracker admin says: "When the same torrent is opened and
1683 * closed and opened again without quitting Transmission ...
1684 * change the peerid. It would help sometimes if a stopped event
1685 * was missed to ensure that we didn't think someone was cheating. */
1686 tr_peerIdInit (tor->peer_id);
1687 tor->isRunning = 1;
1688 tr_torrentSetDirty (tor);
1689 tr_runInEventThread (tor->session, torrentStartImpl, tor);
1691 tr_sessionUnlock (tor->session);
1694 void
1695 tr_torrentStart (tr_torrent * tor)
1697 if (tr_isTorrent (tor))
1698 torrentStart (tor, false);
1701 void
1702 tr_torrentStartNow (tr_torrent * tor)
1704 if (tr_isTorrent (tor))
1705 torrentStart (tor, true);
1708 static void
1709 torrentRecheckDoneImpl (void * vtor)
1711 tr_torrent * tor = vtor;
1712 assert (tr_isTorrent (tor));
1714 tr_torrentRecheckCompleteness (tor);
1716 if (tor->startAfterVerify) {
1717 tor->startAfterVerify = false;
1718 torrentStart (tor, false);
1722 static void
1723 torrentRecheckDoneCB (tr_torrent * tor)
1725 assert (tr_isTorrent (tor));
1727 tr_runInEventThread (tor->session, torrentRecheckDoneImpl, tor);
1730 static void
1731 verifyTorrent (void * vtor)
1733 bool startAfter;
1734 tr_torrent * tor = vtor;
1736 tr_sessionLock (tor->session);
1738 /* if the torrent's already being verified, stop it */
1739 tr_verifyRemove (tor);
1741 startAfter = (tor->isRunning || tor->startAfterVerify) && !tor->isStopping;
1743 if (tor->isRunning)
1744 tr_torrentStop (tor);
1746 tor->startAfterVerify = startAfter;
1748 if (setLocalErrorIfFilesDisappeared (tor))
1749 tor->startAfterVerify = false;
1750 else
1751 tr_verifyAdd (tor, torrentRecheckDoneCB);
1753 tr_sessionUnlock (tor->session);
1756 void
1757 tr_torrentVerify (tr_torrent * tor)
1759 if (tr_isTorrent (tor))
1760 tr_runInEventThread (tor->session, verifyTorrent, tor);
1763 void
1764 tr_torrentSave (tr_torrent * tor)
1766 assert (tr_isTorrent (tor));
1768 if (tor->isDirty)
1770 tor->isDirty = false;
1771 tr_torrentSaveResume (tor);
1775 static void
1776 stopTorrent (void * vtor)
1778 tr_torrent * tor = vtor;
1779 tr_torinf (tor, "Pausing");
1781 assert (tr_isTorrent (tor));
1783 tr_torrentLock (tor);
1785 tr_verifyRemove (tor);
1786 torrentSetQueued (tor, false);
1787 tr_peerMgrStopTorrent (tor);
1788 tr_announcerTorrentStopped (tor);
1789 tr_cacheFlushTorrent (tor->session->cache, tor);
1791 tr_fdTorrentClose (tor->session, tor->uniqueId);
1793 if (!tor->isDeleting)
1794 tr_torrentSave (tor);
1796 tr_torrentUnlock (tor);
1799 void
1800 tr_torrentStop (tr_torrent * tor)
1802 assert (tr_isTorrent (tor));
1804 if (tr_isTorrent (tor))
1806 tr_sessionLock (tor->session);
1808 tor->isRunning = 0;
1809 tor->isStopping = 0;
1810 tr_torrentSetDirty (tor);
1811 tr_runInEventThread (tor->session, stopTorrent, tor);
1813 tr_sessionUnlock (tor->session);
1817 static void
1818 closeTorrent (void * vtor)
1820 tr_benc * d;
1821 tr_torrent * tor = vtor;
1823 assert (tr_isTorrent (tor));
1825 d = tr_bencListAddDict (&tor->session->removedTorrents, 2);
1826 tr_bencDictAddInt (d, "id", tor->uniqueId);
1827 tr_bencDictAddInt (d, "date", tr_time ());
1829 tr_torinf (tor, "%s", _("Removing torrent"));
1831 stopTorrent (tor);
1833 if (tor->isDeleting)
1835 tr_metainfoRemoveSaved (tor->session, &tor->info);
1836 tr_torrentRemoveResume (tor);
1839 tor->isRunning = 0;
1840 freeTorrent (tor);
1843 void
1844 tr_torrentFree (tr_torrent * tor)
1846 if (tr_isTorrent (tor))
1848 tr_session * session = tor->session;
1849 assert (tr_isSession (session));
1850 tr_sessionLock (session);
1852 tr_torrentClearCompletenessCallback (tor);
1853 tr_runInEventThread (session, closeTorrent, tor);
1855 tr_sessionUnlock (session);
1859 struct remove_data
1861 tr_torrent * tor;
1862 bool deleteFlag;
1863 tr_fileFunc * deleteFunc;
1866 static void tr_torrentDeleteLocalData (tr_torrent *, tr_fileFunc);
1868 static void
1869 removeTorrent (void * vdata)
1871 struct remove_data * data = vdata;
1873 if (data->deleteFlag)
1874 tr_torrentDeleteLocalData (data->tor, data->deleteFunc);
1876 tr_torrentClearCompletenessCallback (data->tor);
1877 closeTorrent (data->tor);
1878 tr_free (data);
1881 void
1882 tr_torrentRemove (tr_torrent * tor,
1883 bool deleteFlag,
1884 tr_fileFunc deleteFunc)
1886 struct remove_data * data;
1888 assert (tr_isTorrent (tor));
1889 tor->isDeleting = 1;
1891 data = tr_new0 (struct remove_data, 1);
1892 data->tor = tor;
1893 data->deleteFlag = deleteFlag;
1894 data->deleteFunc = deleteFunc;
1895 tr_runInEventThread (tor->session, removeTorrent, data);
1899 *** Completeness
1902 static const char *
1903 getCompletionString (int type)
1905 switch (type)
1907 /* Translators: this is a minor point that's safe to skip over, but FYI:
1908 "Complete" and "Done" are specific, different terms in Transmission:
1909 "Complete" means we've downloaded every file in the torrent.
1910 "Done" means we're done downloading the files we wanted, but NOT all
1911 that exist */
1912 case TR_PARTIAL_SEED:
1913 return _("Done");
1915 case TR_SEED:
1916 return _("Complete");
1918 default:
1919 return _("Incomplete");
1923 static void
1924 fireCompletenessChange (tr_torrent * tor,
1925 tr_completeness status,
1926 bool wasRunning)
1928 assert ((status == TR_LEECH)
1929 || (status == TR_SEED)
1930 || (status == TR_PARTIAL_SEED));
1932 if (tor->completeness_func)
1933 tor->completeness_func (tor, status, wasRunning,
1934 tor->completeness_func_user_data);
1937 void
1938 tr_torrentSetCompletenessCallback (tr_torrent * tor,
1939 tr_torrent_completeness_func func,
1940 void * user_data)
1942 assert (tr_isTorrent (tor));
1944 tor->completeness_func = func;
1945 tor->completeness_func_user_data = user_data;
1948 void
1949 tr_torrentClearCompletenessCallback (tr_torrent * torrent)
1951 tr_torrentSetCompletenessCallback (torrent, NULL, NULL);
1954 void
1955 tr_torrentSetRatioLimitHitCallback (tr_torrent * tor,
1956 tr_torrent_ratio_limit_hit_func func,
1957 void * user_data)
1959 assert (tr_isTorrent (tor));
1961 tor->ratio_limit_hit_func = func;
1962 tor->ratio_limit_hit_func_user_data = user_data;
1965 void
1966 tr_torrentClearRatioLimitHitCallback (tr_torrent * torrent)
1968 tr_torrentSetRatioLimitHitCallback (torrent, NULL, NULL);
1971 void
1972 tr_torrentSetIdleLimitHitCallback (tr_torrent * tor,
1973 tr_torrent_idle_limit_hit_func func,
1974 void * user_data)
1976 assert (tr_isTorrent (tor));
1978 tor->idle_limit_hit_func = func;
1979 tor->idle_limit_hit_func_user_data = user_data;
1982 void
1983 tr_torrentClearIdleLimitHitCallback (tr_torrent * torrent)
1985 tr_torrentSetIdleLimitHitCallback (torrent, NULL, NULL);
1988 static void
1989 onSigCHLD (int i UNUSED)
1991 waitpid (-1, NULL, WNOHANG);
1994 static void
1995 torrentCallScript (const tr_torrent * tor, const char * script)
1997 char timeStr[128];
1998 const time_t now = tr_time ();
2000 tr_strlcpy (timeStr, ctime (&now), sizeof (timeStr));
2001 *strchr (timeStr,'\n') = '\0';
2003 if (script && *script)
2005 int i;
2006 char * cmd[] = { tr_strdup (script), NULL };
2007 char * env[] = {
2008 tr_strdup_printf ("TR_APP_VERSION=%s", SHORT_VERSION_STRING),
2009 tr_strdup_printf ("TR_TIME_LOCALTIME=%s", timeStr),
2010 tr_strdup_printf ("TR_TORRENT_DIR=%s", tor->currentDir),
2011 tr_strdup_printf ("TR_TORRENT_ID=%d", tr_torrentId (tor)),
2012 tr_strdup_printf ("TR_TORRENT_HASH=%s", tor->info.hashString),
2013 tr_strdup_printf ("TR_TORRENT_NAME=%s", tr_torrentName (tor)),
2014 NULL };
2016 tr_torinf (tor, "Calling script \"%s\"", script);
2018 #ifdef WIN32
2019 if (_spawnvpe (_P_NOWAIT, script, (const char*)cmd, env) == -1)
2020 tr_torerr (tor, "error executing script \"%s\": %s", cmd[0], tr_strerror (errno));
2021 #else
2022 signal (SIGCHLD, onSigCHLD);
2024 if (!fork ())
2026 for (i=0; env[i]; ++i)
2027 putenv (env[i]);
2029 if (execvp (script, cmd) == -1)
2030 tr_torerr (tor, "error executing script \"%s\": %s", cmd[0], tr_strerror (errno));
2032 _exit (0);
2034 #endif
2036 for (i=0; cmd[i]; ++i) tr_free (cmd[i]);
2037 for (i=0; env[i]; ++i) tr_free (env[i]);
2041 void
2042 tr_torrentRecheckCompleteness (tr_torrent * tor)
2044 tr_completeness completeness;
2046 assert (tr_isTorrent (tor));
2048 tr_torrentLock (tor);
2050 completeness = tr_cpGetStatus (&tor->completion);
2052 if (completeness != tor->completeness)
2054 const int recentChange = tor->downloadedCur != 0;
2055 const bool wasLeeching = !tr_torrentIsSeed (tor);
2056 const bool wasRunning = tor->isRunning;
2058 if (recentChange)
2060 tr_torinf (tor, _("State changed from \"%1$s\" to \"%2$s\""),
2061 getCompletionString (tor->completeness),
2062 getCompletionString (completeness));
2065 tor->completeness = completeness;
2066 tr_fdTorrentClose (tor->session, tor->uniqueId);
2068 fireCompletenessChange (tor, completeness, wasRunning);
2070 if (tr_torrentIsSeed (tor))
2072 if (recentChange)
2074 tr_announcerTorrentCompleted (tor);
2075 tor->doneDate = tor->anyDate = tr_time ();
2078 if (wasLeeching && wasRunning)
2080 /* clear interested flag on all peers */
2081 tr_peerMgrClearInterest (tor);
2083 /* if completeness was TR_LEECH then the seed limit check will have been skipped in bandwidthPulse */
2084 tr_torrentCheckSeedLimit (tor);
2087 if (tor->currentDir == tor->incompleteDir)
2088 tr_torrentSetLocation (tor, tor->downloadDir, true, NULL, NULL);
2090 if (tr_sessionIsTorrentDoneScriptEnabled (tor->session))
2091 torrentCallScript (tor, tr_sessionGetTorrentDoneScript (tor->session));
2094 tr_torrentSetDirty (tor);
2097 tr_torrentUnlock (tor);
2100 /***
2101 ****
2102 ***/
2104 static void
2105 tr_torrentFireMetadataCompleted (tr_torrent * tor)
2107 assert (tr_isTorrent (tor));
2109 if (tor->metadata_func)
2110 tor->metadata_func (tor, tor->metadata_func_user_data);
2113 void
2114 tr_torrentSetMetadataCallback (tr_torrent * tor,
2115 tr_torrent_metadata_func func,
2116 void * user_data)
2118 assert (tr_isTorrent (tor));
2120 tor->metadata_func = func;
2121 tor->metadata_func_user_data = user_data;
2126 *** File priorities
2129 void
2130 tr_torrentInitFilePriority (tr_torrent * tor,
2131 tr_file_index_t fileIndex,
2132 tr_priority_t priority)
2134 tr_piece_index_t i;
2135 tr_file * file;
2137 assert (tr_isTorrent (tor));
2138 assert (fileIndex < tor->info.fileCount);
2139 assert (tr_isPriority (priority));
2141 file = &tor->info.files[fileIndex];
2142 file->priority = priority;
2143 for (i = file->firstPiece; i <= file->lastPiece; ++i)
2144 tor->info.pieces[i].priority = calculatePiecePriority (tor, i, fileIndex);
2147 void
2148 tr_torrentSetFilePriorities (tr_torrent * tor,
2149 const tr_file_index_t * files,
2150 tr_file_index_t fileCount,
2151 tr_priority_t priority)
2153 tr_file_index_t i;
2154 assert (tr_isTorrent (tor));
2155 tr_torrentLock (tor);
2157 for (i = 0; i < fileCount; ++i)
2158 if (files[i] < tor->info.fileCount)
2159 tr_torrentInitFilePriority (tor, files[i], priority);
2160 tr_torrentSetDirty (tor);
2161 tr_peerMgrRebuildRequests (tor);
2163 tr_torrentUnlock (tor);
2166 tr_priority_t*
2167 tr_torrentGetFilePriorities (const tr_torrent * tor)
2169 tr_file_index_t i;
2170 tr_priority_t * p;
2172 assert (tr_isTorrent (tor));
2174 tr_torrentLock (tor);
2175 p = tr_new0 (tr_priority_t, tor->info.fileCount);
2176 for (i = 0; i < tor->info.fileCount; ++i)
2177 p[i] = tor->info.files[i].priority;
2178 tr_torrentUnlock (tor);
2180 return p;
2184 *** File DND
2187 static void
2188 setFileDND (tr_torrent * tor, tr_file_index_t fileIndex, int doDownload)
2190 const int8_t dnd = !doDownload;
2191 tr_piece_index_t firstPiece;
2192 int8_t firstPieceDND;
2193 tr_piece_index_t lastPiece;
2194 int8_t lastPieceDND;
2195 tr_file_index_t i;
2196 tr_file * file = &tor->info.files[fileIndex];
2198 file->dnd = dnd;
2199 firstPiece = file->firstPiece;
2200 lastPiece = file->lastPiece;
2202 /* can't set the first piece to DND unless
2203 every file using that piece is DND */
2204 firstPieceDND = dnd;
2205 if (fileIndex > 0)
2207 for (i = fileIndex - 1; firstPieceDND; --i)
2209 if (tor->info.files[i].lastPiece != firstPiece)
2210 break;
2211 firstPieceDND = tor->info.files[i].dnd;
2212 if (!i)
2213 break;
2217 /* can't set the last piece to DND unless
2218 every file using that piece is DND */
2219 lastPieceDND = dnd;
2220 for (i = fileIndex + 1; lastPieceDND && i < tor->info.fileCount; ++i)
2222 if (tor->info.files[i].firstPiece != lastPiece)
2223 break;
2224 lastPieceDND = tor->info.files[i].dnd;
2227 if (firstPiece == lastPiece)
2229 tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
2231 else
2233 tr_piece_index_t pp;
2234 tor->info.pieces[firstPiece].dnd = firstPieceDND;
2235 tor->info.pieces[lastPiece].dnd = lastPieceDND;
2236 for (pp = firstPiece + 1; pp < lastPiece; ++pp)
2237 tor->info.pieces[pp].dnd = dnd;
2241 void
2242 tr_torrentInitFileDLs (tr_torrent * tor,
2243 const tr_file_index_t * files,
2244 tr_file_index_t fileCount,
2245 bool doDownload)
2247 tr_file_index_t i;
2249 assert (tr_isTorrent (tor));
2251 tr_torrentLock (tor);
2253 for (i=0; i<fileCount; ++i)
2254 if (files[i] < tor->info.fileCount)
2255 setFileDND (tor, files[i], doDownload);
2257 tr_cpInvalidateDND (&tor->completion);
2259 tr_torrentUnlock (tor);
2262 void
2263 tr_torrentSetFileDLs (tr_torrent * tor,
2264 const tr_file_index_t * files,
2265 tr_file_index_t fileCount,
2266 bool doDownload)
2268 assert (tr_isTorrent (tor));
2269 tr_torrentLock (tor);
2271 tr_torrentInitFileDLs (tor, files, fileCount, doDownload);
2272 tr_torrentSetDirty (tor);
2273 tr_peerMgrRebuildRequests (tor);
2275 tr_torrentUnlock (tor);
2278 /***
2279 ****
2280 ***/
2282 tr_priority_t
2283 tr_torrentGetPriority (const tr_torrent * tor)
2285 assert (tr_isTorrent (tor));
2287 return tor->bandwidth.priority;
2290 void
2291 tr_torrentSetPriority (tr_torrent * tor, tr_priority_t priority)
2293 assert (tr_isTorrent (tor));
2294 assert (tr_isPriority (priority));
2296 if (tor->bandwidth.priority != priority)
2298 tor->bandwidth.priority = priority;
2300 tr_torrentSetDirty (tor);
2304 /***
2305 ****
2306 ***/
2308 void
2309 tr_torrentSetPeerLimit (tr_torrent * tor,
2310 uint16_t maxConnectedPeers)
2312 assert (tr_isTorrent (tor));
2314 if (tor->maxConnectedPeers != maxConnectedPeers)
2316 tor->maxConnectedPeers = maxConnectedPeers;
2318 tr_torrentSetDirty (tor);
2322 uint16_t
2323 tr_torrentGetPeerLimit (const tr_torrent * tor)
2325 assert (tr_isTorrent (tor));
2327 return tor->maxConnectedPeers;
2330 /***
2331 ****
2332 ***/
2334 void
2335 tr_torrentGetBlockLocation (const tr_torrent * tor,
2336 tr_block_index_t block,
2337 tr_piece_index_t * piece,
2338 uint32_t * offset,
2339 uint32_t * length)
2341 uint64_t pos = block;
2342 pos *= tor->blockSize;
2343 *piece = pos / tor->info.pieceSize;
2344 *offset = pos - (*piece * tor->info.pieceSize);
2345 *length = tr_torBlockCountBytes (tor, block);
2349 tr_block_index_t
2350 _tr_block (const tr_torrent * tor,
2351 tr_piece_index_t index,
2352 uint32_t offset)
2354 tr_block_index_t ret;
2356 assert (tr_isTorrent (tor));
2358 ret = index;
2359 ret *= (tor->info.pieceSize / tor->blockSize);
2360 ret += offset / tor->blockSize;
2361 return ret;
2364 bool
2365 tr_torrentReqIsValid (const tr_torrent * tor,
2366 tr_piece_index_t index,
2367 uint32_t offset,
2368 uint32_t length)
2370 int err = 0;
2372 assert (tr_isTorrent (tor));
2374 if (index >= tor->info.pieceCount)
2375 err = 1;
2376 else if (length < 1)
2377 err = 2;
2378 else if ((offset + length) > tr_torPieceCountBytes (tor, index))
2379 err = 3;
2380 else if (length > MAX_BLOCK_SIZE)
2381 err = 4;
2382 else if (tr_pieceOffset (tor, index, offset, length) > tor->info.totalSize)
2383 err = 5;
2385 if (err) tr_tordbg (tor, "index %lu offset %lu length %lu err %d\n",
2386 (unsigned long)index,
2387 (unsigned long)offset,
2388 (unsigned long)length,
2389 err);
2391 return !err;
2394 uint64_t
2395 tr_pieceOffset (const tr_torrent * tor,
2396 tr_piece_index_t index,
2397 uint32_t offset,
2398 uint32_t length)
2400 uint64_t ret;
2402 assert (tr_isTorrent (tor));
2404 ret = tor->info.pieceSize;
2405 ret *= index;
2406 ret += offset;
2407 ret += length;
2408 return ret;
2411 void
2412 tr_torGetFileBlockRange (const tr_torrent * tor,
2413 const tr_file_index_t file,
2414 tr_block_index_t * first,
2415 tr_block_index_t * last)
2417 const tr_file * f = &tor->info.files[file];
2418 uint64_t offset = f->offset;
2419 *first = offset / tor->blockSize;
2420 if (!f->length)
2421 *last = *first;
2422 else {
2423 offset += f->length - 1;
2424 *last = offset / tor->blockSize;
2428 void
2429 tr_torGetPieceBlockRange (const tr_torrent * tor,
2430 const tr_piece_index_t piece,
2431 tr_block_index_t * first,
2432 tr_block_index_t * last)
2434 uint64_t offset = tor->info.pieceSize;
2435 offset *= piece;
2436 *first = offset / tor->blockSize;
2437 offset += (tr_torPieceCountBytes (tor, piece) - 1);
2438 *last = offset / tor->blockSize;
2442 /***
2443 ****
2444 ***/
2446 void
2447 tr_torrentSetPieceChecked (tr_torrent * tor, tr_piece_index_t pieceIndex)
2449 assert (tr_isTorrent (tor));
2450 assert (pieceIndex < tor->info.pieceCount);
2452 tor->info.pieces[pieceIndex].timeChecked = tr_time ();
2455 void
2456 tr_torrentSetChecked (tr_torrent * tor, time_t when)
2458 tr_piece_index_t i, n;
2460 assert (tr_isTorrent (tor));
2462 for (i=0, n=tor->info.pieceCount; i!=n; ++i)
2463 tor->info.pieces[i].timeChecked = when;
2466 bool
2467 tr_torrentCheckPiece (tr_torrent * tor, tr_piece_index_t pieceIndex)
2469 const bool pass = tr_ioTestPiece (tor, pieceIndex);
2471 tr_deeplog_tor (tor, "[LAZY] tr_torrentCheckPiece tested piece %zu, pass==%d", (size_t)pieceIndex, (int)pass);
2472 tr_torrentSetHasPiece (tor, pieceIndex, pass);
2473 tr_torrentSetPieceChecked (tor, pieceIndex);
2474 tor->anyDate = tr_time ();
2475 tr_torrentSetDirty (tor);
2477 return pass;
2480 time_t
2481 tr_torrentGetFileMTime (const tr_torrent * tor, tr_file_index_t i)
2483 time_t mtime = 0;
2484 if (!tr_fdFileGetCachedMTime (tor->session, tor->uniqueId, i, &mtime))
2485 tr_torrentFindFile2 (tor, i, NULL, NULL, &mtime);
2486 return mtime;
2489 bool
2490 tr_torrentPieceNeedsCheck (const tr_torrent * tor, tr_piece_index_t p)
2492 uint64_t unused;
2493 tr_file_index_t f;
2494 const tr_info * inf = tr_torrentInfo (tor);
2496 /* if we've never checked this piece, then it needs to be checked */
2497 if (!inf->pieces[p].timeChecked)
2498 return true;
2500 /* If we think we've completed one of the files in this piece,
2501 * but it's been modified since we last checked it,
2502 * then it needs to be rechecked */
2503 tr_ioFindFileLocation (tor, p, 0, &f, &unused);
2504 for (; f < inf->fileCount && pieceHasFile (p, &inf->files[f]); ++f)
2505 if (tr_cpFileIsComplete (&tor->completion, f))
2506 if (tr_torrentGetFileMTime (tor, f) > inf->pieces[p].timeChecked)
2507 return true;
2509 return false;
2512 /***
2513 ****
2514 ***/
2516 static int
2517 compareTrackerByTier (const void * va, const void * vb)
2519 const tr_tracker_info * a = va;
2520 const tr_tracker_info * b = vb;
2522 /* sort by tier */
2523 if (a->tier != b->tier)
2524 return a->tier - b->tier;
2526 /* get the effects of a stable sort by comparing the two elements' addresses */
2527 return a - b;
2530 bool
2531 tr_torrentSetAnnounceList (tr_torrent * tor,
2532 const tr_tracker_info * trackers_in,
2533 int trackerCount)
2535 int i;
2536 tr_benc metainfo;
2537 bool ok = true;
2538 tr_tracker_info * trackers;
2540 tr_torrentLock (tor);
2542 assert (tr_isTorrent (tor));
2544 /* ensure the trackers' tiers are in ascending order */
2545 trackers = tr_memdup (trackers_in, sizeof (tr_tracker_info) * trackerCount);
2546 qsort (trackers, trackerCount, sizeof (tr_tracker_info), compareTrackerByTier);
2548 /* look for bad URLs */
2549 for (i=0; ok && i<trackerCount; ++i)
2550 if (!tr_urlIsValidTracker (trackers[i].announce))
2551 ok = false;
2553 /* save to the .torrent file */
2554 if (ok && !tr_bencLoadFile (&metainfo, TR_FMT_BENC, tor->info.torrent))
2556 bool hasInfo;
2557 tr_info tmpInfo;
2559 /* remove the old fields */
2560 tr_bencDictRemove (&metainfo, "announce");
2561 tr_bencDictRemove (&metainfo, "announce-list");
2563 /* add the new fields */
2564 if (trackerCount > 0)
2566 tr_bencDictAddStr (&metainfo, "announce", trackers[0].announce);
2568 if (trackerCount > 1)
2570 int i;
2571 int prevTier = -1;
2572 tr_benc * tier = NULL;
2573 tr_benc * announceList = tr_bencDictAddList (&metainfo, "announce-list", 0);
2575 for (i=0; i<trackerCount; ++i) {
2576 if (prevTier != trackers[i].tier) {
2577 prevTier = trackers[i].tier;
2578 tier = tr_bencListAddList (announceList, 0);
2580 tr_bencListAddStr (tier, trackers[i].announce);
2584 /* try to parse it back again, to make sure it's good */
2585 memset (&tmpInfo, 0, sizeof (tr_info));
2586 if (tr_metainfoParse (tor->session, &metainfo, &tmpInfo,
2587 &hasInfo, &tor->infoDictLength))
2589 /* it's good, so keep these new trackers and free the old ones */
2591 tr_info swap;
2592 swap.trackers = tor->info.trackers;
2593 swap.trackerCount = tor->info.trackerCount;
2594 tor->info.trackers = tmpInfo.trackers;
2595 tor->info.trackerCount = tmpInfo.trackerCount;
2596 tmpInfo.trackers = swap.trackers;
2597 tmpInfo.trackerCount = swap.trackerCount;
2599 tr_metainfoFree (&tmpInfo);
2600 tr_bencToFile (&metainfo, TR_FMT_BENC, tor->info.torrent);
2603 /* cleanup */
2604 tr_bencFree (&metainfo);
2606 /* if we had a tracker-related error on this torrent,
2607 * and that tracker's been removed,
2608 * then clear the error */
2609 if ((tor->error == TR_STAT_TRACKER_WARNING)
2610 || (tor->error == TR_STAT_TRACKER_ERROR))
2612 bool clear = true;
2614 for (i=0; clear && i<trackerCount; ++i)
2615 if (!strcmp (trackers[i].announce, tor->errorTracker))
2616 clear = false;
2618 if (clear)
2619 tr_torrentClearError (tor);
2622 /* tell the announcer to reload this torrent's tracker list */
2623 tr_announcerResetTorrent (tor->session->announcer, tor);
2626 tr_torrentUnlock (tor);
2628 tr_free (trackers);
2629 return ok;
2636 void
2637 tr_torrentSetAddedDate (tr_torrent * tor,
2638 time_t t)
2640 assert (tr_isTorrent (tor));
2642 tor->addedDate = t;
2643 tor->anyDate = MAX (tor->anyDate, tor->addedDate);
2646 void
2647 tr_torrentSetActivityDate (tr_torrent * tor, time_t t)
2649 assert (tr_isTorrent (tor));
2651 tor->activityDate = t;
2652 tor->anyDate = MAX (tor->anyDate, tor->activityDate);
2655 void
2656 tr_torrentSetDoneDate (tr_torrent * tor,
2657 time_t t)
2659 assert (tr_isTorrent (tor));
2661 tor->doneDate = t;
2662 tor->anyDate = MAX (tor->anyDate, tor->doneDate);
2669 uint64_t
2670 tr_torrentGetBytesLeftToAllocate (const tr_torrent * tor)
2672 tr_file_index_t i;
2673 uint64_t bytesLeft = 0;
2675 assert (tr_isTorrent (tor));
2677 for (i=0; i<tor->info.fileCount; ++i)
2679 if (!tor->info.files[i].dnd)
2681 struct stat sb;
2682 const uint64_t length = tor->info.files[i].length;
2683 char * path = tr_torrentFindFile (tor, i);
2685 bytesLeft += length;
2687 if ((path != NULL) && !stat (path, &sb)
2688 && S_ISREG (sb.st_mode)
2689 && ((uint64_t)sb.st_size <= length))
2690 bytesLeft -= sb.st_size;
2692 tr_free (path);
2696 return bytesLeft;
2699 /****
2700 ***** Removing the torrent's local data
2701 ****/
2703 static bool
2704 isJunkFile (const char * base)
2706 int i;
2707 static const char * files[] = { ".DS_Store", "desktop.ini", "Thumbs.db" };
2708 static const int file_count = sizeof (files) / sizeof (files[0]);
2710 for (i=0; i<file_count; ++i)
2711 if (!strcmp (base, files[i]))
2712 return true;
2714 #ifdef SYS_DARWIN
2715 /* check for resource forks. <http://support.apple.com/kb/TA20578> */
2716 if (!memcmp (base, "._", 2))
2717 return true;
2718 #endif
2720 return false;
2723 static void
2724 removeEmptyFoldersAndJunkFiles (const char * folder)
2726 DIR * odir;
2727 if ((odir = opendir (folder))) {
2728 struct dirent * d;
2729 while ((d = readdir (odir))) {
2730 if (strcmp (d->d_name, ".") && strcmp (d->d_name, "..")) {
2731 struct stat sb;
2732 char * filename = tr_buildPath (folder, d->d_name, NULL);
2733 if (!stat (filename, &sb) && S_ISDIR (sb.st_mode))
2734 removeEmptyFoldersAndJunkFiles (filename);
2735 else if (isJunkFile (d->d_name))
2736 remove (filename);
2737 tr_free (filename);
2740 remove (folder);
2741 closedir (odir);
2746 * This convoluted code does something (seemingly) simple:
2747 * remove the torrent's local files.
2749 * Fun complications:
2750 * 1. Try to preserve the directory hierarchy in the recycle bin.
2751 * 2. If there are nontorrent files, don't delete them...
2752 * 3. ...unless the other files are "junk", such as .DS_Store
2754 static void
2755 deleteLocalData (tr_torrent * tor, tr_fileFunc func)
2757 int i, n;
2758 tr_file_index_t f;
2759 char * base;
2760 DIR * odir;
2761 char * tmpdir = NULL;
2762 tr_ptrArray files = TR_PTR_ARRAY_INIT;
2763 tr_ptrArray folders = TR_PTR_ARRAY_INIT;
2764 const void * const vstrcmp = strcmp;
2765 const char * const top = tor->currentDir;
2767 /* if it's a magnet link, there's nothing to move... */
2768 if (!tr_torrentHasMetadata (tor))
2769 return;
2771 /***
2772 **** Move the local data to a new tmpdir
2773 ***/
2775 base = tr_strdup_printf ("%s__XXXXXX", tr_torrentName (tor));
2776 tmpdir = tr_buildPath (top, base, NULL);
2777 tr_mkdtemp (tmpdir);
2778 tr_free (base);
2780 for (f=0; f<tor->info.fileCount; ++f)
2782 char * filename = tr_buildPath (top, tor->info.files[f].name, NULL);
2783 if (!tr_fileExists (filename, NULL)) {
2784 char * partial = tr_torrentBuildPartial (tor, f);
2785 tr_free (filename);
2786 filename = tr_buildPath (top, partial, NULL);
2787 tr_free (partial);
2788 if (!tr_fileExists (filename, NULL)) {
2789 tr_free (filename);
2790 filename = NULL;
2794 if (filename != NULL)
2796 char * target = tr_buildPath (tmpdir, tor->info.files[f].name, NULL);
2797 char * target_dir = tr_dirname (target);
2798 tr_mkdirp (target_dir, 0777);
2799 rename (filename, target);
2800 tr_ptrArrayAppend (&files, target);
2801 tr_free (target_dir);
2802 tr_free (filename);
2806 /***
2807 **** Remove tmpdir.
2808 ****
2809 **** Try deleting the top-level files & folders to preserve
2810 **** the directory hierarchy in the recycle bin.
2811 **** If case that fails -- for example, rmdir () doesn't
2812 **** delete nonempty folders -- go from the bottom up too.
2813 ***/
2815 /* try deleting the local data's top-level files & folders */
2816 if ((odir = opendir (tmpdir)))
2818 struct dirent * d;
2819 while ((d = readdir (odir)))
2821 if (strcmp (d->d_name, ".") && strcmp (d->d_name, ".."))
2823 char * file = tr_buildPath (tmpdir, d->d_name, NULL);
2824 func (file);
2825 tr_free (file);
2828 closedir (odir);
2831 /* go from the bottom up */
2832 for (i=0, n=tr_ptrArraySize (&files); i<n; ++i)
2834 char * walk = tr_strdup (tr_ptrArrayNth (&files, i));
2835 while (tr_fileExists (walk, NULL) && !tr_is_same_file (tmpdir, walk))
2837 char * tmp = tr_dirname (walk);
2838 func (walk);
2839 tr_free (walk);
2840 walk = tmp;
2842 tr_free (walk);
2845 /***
2846 **** The local data has been removed.
2847 **** What's left in top are empty folders, junk, and user-generated files.
2848 **** Remove the first two categories and leave the third.
2849 ***/
2851 /* build a list of 'top's child directories that belong to this torrent */
2852 for (f=0; f<tor->info.fileCount; ++f)
2854 /* get the directory that this file goes in... */
2855 char * filename = tr_buildPath (top, tor->info.files[f].name, NULL);
2856 char * dir = tr_dirname (filename);
2857 if (!tr_is_same_file (top, dir) && strcmp (top, dir)) {
2858 for (;;) {
2859 char * parent = tr_dirname (dir);
2860 if (tr_is_same_file (top, parent) || !strcmp (top, parent)) {
2861 if (tr_ptrArrayFindSorted (&folders, dir, vstrcmp) == NULL) {
2862 tr_ptrArrayInsertSorted (&folders, tr_strdup (dir), vstrcmp);
2864 break;
2866 tr_free (dir);
2867 dir = parent;
2870 tr_free (dir);
2871 tr_free (filename);
2873 for (i=0, n=tr_ptrArraySize (&folders); i<n; ++i)
2874 removeEmptyFoldersAndJunkFiles (tr_ptrArrayNth (&folders, i));
2876 /* cleanup */
2877 rmdir (tmpdir);
2878 tr_free (tmpdir);
2879 tr_ptrArrayDestruct (&folders, tr_free);
2880 tr_ptrArrayDestruct (&files, tr_free);
2883 static void
2884 tr_torrentDeleteLocalData (tr_torrent * tor, tr_fileFunc func)
2886 assert (tr_isTorrent (tor));
2888 if (func == NULL)
2889 func = remove;
2891 /* close all the files because we're about to delete them */
2892 tr_cacheFlushTorrent (tor->session->cache, tor);
2893 tr_fdTorrentClose (tor->session, tor->uniqueId);
2895 deleteLocalData (tor, func);
2898 /***
2899 ****
2900 ***/
2902 struct LocationData
2904 bool move_from_old_location;
2905 volatile int * setme_state;
2906 volatile double * setme_progress;
2907 char * location;
2908 tr_torrent * tor;
2911 static void
2912 setLocation (void * vdata)
2914 bool err = false;
2915 struct LocationData * data = vdata;
2916 tr_torrent * tor = data->tor;
2917 const bool do_move = data->move_from_old_location;
2918 const char * location = data->location;
2919 double bytesHandled = 0;
2921 assert (tr_isTorrent (tor));
2923 tr_dbg ("Moving \"%s\" location from currentDir \"%s\" to \"%s\"",
2924 tr_torrentName (tor), tor->currentDir, location);
2926 tr_mkdirp (location, 0777);
2928 if (!tr_is_same_file (location, tor->currentDir))
2930 tr_file_index_t i;
2932 /* bad idea to move files while they're being verified... */
2933 tr_verifyRemove (tor);
2935 /* try to move the files.
2936 * FIXME: there are still all kinds of nasty cases, like what
2937 * if the target directory runs out of space halfway through... */
2938 for (i=0; !err && i<tor->info.fileCount; ++i)
2940 const tr_file * f = &tor->info.files[i];
2941 const char * oldbase;
2942 char * sub;
2943 if (tr_torrentFindFile2 (tor, i, &oldbase, &sub, NULL))
2945 char * oldpath = tr_buildPath (oldbase, sub, NULL);
2946 char * newpath = tr_buildPath (location, sub, NULL);
2948 tr_dbg ("Found file #%d: %s", (int)i, oldpath);
2950 if (do_move && !tr_is_same_file (oldpath, newpath))
2952 bool renamed = false;
2953 errno = 0;
2954 tr_torinf (tor, "moving \"%s\" to \"%s\"", oldpath, newpath);
2955 if (tr_moveFile (oldpath, newpath, &renamed))
2957 err = true;
2958 tr_torerr (tor, "error moving \"%s\" to \"%s\": %s",
2959 oldpath, newpath, tr_strerror (errno));
2963 tr_free (newpath);
2964 tr_free (oldpath);
2965 tr_free (sub);
2968 if (data->setme_progress)
2970 bytesHandled += f->length;
2971 *data->setme_progress = bytesHandled / tor->info.totalSize;
2975 if (!err)
2977 /* blow away the leftover subdirectories in the old location */
2978 if (do_move)
2979 tr_torrentDeleteLocalData (tor, remove);
2981 /* set the new location and reverify */
2982 tr_torrentSetDownloadDir (tor, location);
2986 if (!err && do_move)
2988 tr_free (tor->incompleteDir);
2989 tor->incompleteDir = NULL;
2990 tor->currentDir = tor->downloadDir;
2993 if (data->setme_state)
2994 *data->setme_state = err ? TR_LOC_ERROR : TR_LOC_DONE;
2996 /* cleanup */
2997 tr_free (data->location);
2998 tr_free (data);
3001 void
3002 tr_torrentSetLocation (tr_torrent * tor,
3003 const char * location,
3004 bool move_from_old_location,
3005 volatile double * setme_progress,
3006 volatile int * setme_state)
3008 struct LocationData * data;
3010 assert (tr_isTorrent (tor));
3012 if (setme_state)
3013 *setme_state = TR_LOC_MOVING;
3014 if (setme_progress)
3015 *setme_progress = 0;
3017 /* run this in the libtransmission thread */
3018 data = tr_new (struct LocationData, 1);
3019 data->tor = tor;
3020 data->location = tr_strdup (location);
3021 data->move_from_old_location = move_from_old_location;
3022 data->setme_state = setme_state;
3023 data->setme_progress = setme_progress;
3024 tr_runInEventThread (tor->session, setLocation, data);
3027 /***
3028 ****
3029 ***/
3031 void
3032 tr_torrentFileCompleted (tr_torrent * tor, tr_file_index_t fileNum)
3034 char * sub;
3035 const char * base;
3036 const tr_info * inf = &tor->info;
3037 const tr_file * f = &inf->files[fileNum];
3038 tr_piece * p;
3039 const tr_piece * pend;
3040 const time_t now = tr_time ();
3042 /* close the file so that we can reopen in read-only mode as needed */
3043 tr_fdFileClose (tor->session, tor, fileNum);
3045 /* now that the file is complete and closed, we can start watching its
3046 * mtime timestamp for changes to know if we need to reverify pieces */
3047 for (p=&inf->pieces[f->firstPiece], pend=&inf->pieces[f->lastPiece]; p!=pend; ++p)
3048 p->timeChecked = now;
3050 /* if the torrent's current filename isn't the same as the one in the
3051 * metadata -- for example, if it had the ".part" suffix appended to
3052 * it until now -- then rename it to match the one in the metadata */
3053 if (tr_torrentFindFile2 (tor, fileNum, &base, &sub, NULL))
3055 if (strcmp (sub, f->name))
3057 char * oldpath = tr_buildPath (base, sub, NULL);
3058 char * newpath = tr_buildPath (base, f->name, NULL);
3060 if (rename (oldpath, newpath))
3061 tr_torerr (tor, "Error moving \"%s\" to \"%s\": %s", oldpath, newpath, tr_strerror (errno));
3063 tr_free (newpath);
3064 tr_free (oldpath);
3067 tr_free (sub);
3071 /***
3072 ****
3073 ***/
3075 bool
3076 tr_torrentFindFile2 (const tr_torrent * tor, tr_file_index_t fileNum,
3077 const char ** base, char ** subpath, time_t * mtime)
3079 char * part = NULL;
3080 const tr_file * file;
3081 const char * b = NULL;
3082 const char * s = NULL;
3084 assert (tr_isTorrent (tor));
3085 assert (fileNum < tor->info.fileCount);
3087 file = &tor->info.files[fileNum];
3089 if (b == NULL) {
3090 char * filename = tr_buildPath (tor->downloadDir, file->name, NULL);
3091 if (tr_fileExists (filename, mtime)) {
3092 b = tor->downloadDir;
3093 s = file->name;
3095 tr_free (filename);
3098 if ((b == NULL) && (tor->incompleteDir != NULL)) {
3099 char * filename = tr_buildPath (tor->incompleteDir, file->name, NULL);
3100 if (tr_fileExists (filename, mtime)) {
3101 b = tor->incompleteDir;
3102 s = file->name;
3104 tr_free (filename);
3107 if (b == NULL)
3108 part = tr_torrentBuildPartial (tor, fileNum);
3110 if ((b == NULL) && (tor->incompleteDir != NULL)) {
3111 char * filename = tr_buildPath (tor->incompleteDir, part, NULL);
3112 if (tr_fileExists (filename, mtime)) {
3113 b = tor->incompleteDir;
3114 s = part;
3116 tr_free (filename);
3119 if (b == NULL) {
3120 char * filename = tr_buildPath (tor->downloadDir, part, NULL);
3121 if (tr_fileExists (filename, mtime)) {
3122 b = tor->downloadDir;
3123 s = part;
3125 tr_free (filename);
3128 if (base != NULL)
3129 *base = b;
3130 if (subpath != NULL)
3131 *subpath = tr_strdup (s);
3133 tr_free (part);
3134 return b != NULL;
3137 char*
3138 tr_torrentFindFile (const tr_torrent * tor, tr_file_index_t fileNum)
3140 char * subpath;
3141 char * ret = NULL;
3142 const char * base;
3144 if (tr_torrentFindFile2 (tor, fileNum, &base, &subpath, NULL))
3146 ret = tr_buildPath (base, subpath, NULL);
3147 tr_free (subpath);
3150 return ret;
3153 /* Decide whether we should be looking for files in downloadDir or incompleteDir. */
3154 static void
3155 refreshCurrentDir (tr_torrent * tor)
3157 const char * dir = NULL;
3159 if (tor->incompleteDir == NULL)
3160 dir = tor->downloadDir;
3161 else if (!tr_torrentHasMetadata (tor)) /* no files to find */
3162 dir = tor->incompleteDir;
3163 else if (!tr_torrentFindFile2 (tor, 0, &dir, NULL, NULL))
3164 dir = tor->incompleteDir;
3166 assert (dir != NULL);
3167 assert ((dir == tor->downloadDir) || (dir == tor->incompleteDir));
3168 tor->currentDir = dir;
3171 char*
3172 tr_torrentBuildPartial (const tr_torrent * tor, tr_file_index_t fileNum)
3174 return tr_strdup_printf ("%s.part", tor->info.files[fileNum].name);
3177 /***
3178 ****
3179 ***/
3181 static int
3182 compareTorrentByQueuePosition (const void * va, const void * vb)
3184 const tr_torrent * a = * (const tr_torrent **) va;
3185 const tr_torrent * b = * (const tr_torrent **) vb;
3187 return a->queuePosition - b->queuePosition;
3190 #ifndef NDEBUG
3191 static bool
3192 queueIsSequenced (tr_session * session)
3194 int i ;
3195 int n ;
3196 bool is_sequenced = true;
3197 tr_torrent * tor;
3198 tr_torrent ** tmp = tr_new (tr_torrent *, session->torrentCount);
3200 /* get all the torrents */
3201 n = 0;
3202 tor = NULL;
3203 while ((tor = tr_torrentNext (session, tor)))
3204 tmp[n++] = tor;
3206 /* sort them by position */
3207 qsort (tmp, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3209 #if 0
3210 fprintf (stderr, "%s", "queue: ");
3211 for (i=0; i<n; ++i)
3212 fprintf (stderr, "%d ", tmp[i]->queuePosition);
3213 fputc ('\n', stderr);
3214 #endif
3216 /* test them */
3217 for (i=0; is_sequenced && i<n; ++i)
3218 if (tmp[i]->queuePosition != i)
3219 is_sequenced = false;
3221 tr_free (tmp);
3222 return is_sequenced;
3224 #endif
3227 tr_torrentGetQueuePosition (const tr_torrent * tor)
3229 return tor->queuePosition;
3232 void
3233 tr_torrentSetQueuePosition (tr_torrent * tor, int pos)
3235 int back = -1;
3236 tr_torrent * walk;
3237 const int old_pos = tor->queuePosition;
3238 const time_t now = tr_time ();
3240 if (pos < 0)
3241 pos = 0;
3243 tor->queuePosition = -1;
3245 walk = NULL;
3246 while ((walk = tr_torrentNext (tor->session, walk)))
3248 if (old_pos < pos) {
3249 if ((old_pos <= walk->queuePosition) && (walk->queuePosition <= pos)) {
3250 walk->queuePosition--;
3251 walk->anyDate = now;
3255 if (old_pos > pos) {
3256 if ((pos <= walk->queuePosition) && (walk->queuePosition < old_pos)) {
3257 walk->queuePosition++;
3258 walk->anyDate = now;
3262 if (back < walk->queuePosition)
3263 back = walk->queuePosition;
3266 tor->queuePosition = MIN (pos, (back+1));
3267 tor->anyDate = now;
3269 assert (queueIsSequenced (tor->session));
3272 void
3273 tr_torrentsQueueMoveTop (tr_torrent ** torrents_in, int n)
3275 int i;
3276 tr_torrent ** torrents = tr_memdup (torrents_in, sizeof (tr_torrent *) * n);
3277 qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3278 for (i=n-1; i>=0; --i)
3279 tr_torrentSetQueuePosition (torrents[i], 0);
3280 tr_free (torrents);
3283 void
3284 tr_torrentsQueueMoveUp (tr_torrent ** torrents_in, int n)
3286 int i;
3287 tr_torrent ** torrents = tr_memdup (torrents_in, sizeof (tr_torrent *) * n);
3288 qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3289 for (i=0; i<n; ++i)
3290 tr_torrentSetQueuePosition (torrents[i], torrents[i]->queuePosition - 1);
3291 tr_free (torrents);
3294 void
3295 tr_torrentsQueueMoveDown (tr_torrent ** torrents_in, int n)
3297 int i;
3298 tr_torrent ** torrents = tr_memdup (torrents_in, sizeof (tr_torrent *) * n);
3299 qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3300 for (i=n-1; i>=0; --i)
3301 tr_torrentSetQueuePosition (torrents[i], torrents[i]->queuePosition + 1);
3302 tr_free (torrents);
3305 void
3306 tr_torrentsQueueMoveBottom (tr_torrent ** torrents_in, int n)
3308 int i;
3309 tr_torrent ** torrents = tr_memdup (torrents_in, sizeof (tr_torrent *) * n);
3310 qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3311 for (i=0; i<n; ++i)
3312 tr_torrentSetQueuePosition (torrents[i], INT_MAX);
3313 tr_free (torrents);
3316 static void
3317 torrentSetQueued (tr_torrent * tor, bool queued)
3319 assert (tr_isTorrent (tor));
3320 assert (tr_isBool (queued));
3322 if (tr_torrentIsQueued (tor) != queued)
3324 tor->isQueued = queued;
3325 tor->anyDate = tr_time ();
3329 void
3330 tr_torrentSetQueueStartCallback (tr_torrent * torrent, void (*callback)(tr_torrent *, void *), void * user_data)
3332 torrent->queue_started_callback = callback;
3333 torrent->queue_started_user_data = user_data;