source: trunk/libtransmission/torrent.c @ 13627

Last change on this file since 13627 was 13627, checked in by jordan, 9 years ago

(trunk, libT) #5146 'seeding complete callback called twice' -- fixed with patch from x190.

  • Property svn:keywords set to Date Rev Author Id
File size: 85.6 KB
Line 
1/*
2 * This file Copyright (C) Mnemosyne LLC
3 *
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.
9 *
10 * $Id: torrent.c 13627 2012-12-05 22:56:45Z jordan $
11 */
12
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>
24
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 () */
31
32#include <event2/util.h> /* evutil_vsnprintf () */
33
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"
57
58/***
59****
60***/
61
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)
69
70/***
71****
72***/
73
74const char *
75tr_torrentName (const tr_torrent * tor)
76{
77    assert (tr_isTorrent (tor));
78
79    return tor->info.name;
80}
81
82int
83tr_torrentId (const tr_torrent * tor)
84{
85    return tor->uniqueId;
86}
87
88tr_torrent*
89tr_torrentFindFromId (tr_session * session, int id)
90{
91    tr_torrent * tor = NULL;
92
93    while ((tor = tr_torrentNext (session, tor)))
94        if (tor->uniqueId == id)
95            return tor;
96
97    return NULL;
98}
99
100tr_torrent*
101tr_torrentFindFromHashString (tr_session *  session, const char * str)
102{
103    tr_torrent * tor = NULL;
104
105    while ((tor = tr_torrentNext (session, tor)))
106        if (!evutil_ascii_strcasecmp (str, tor->info.hashString))
107            return tor;
108
109    return NULL;
110}
111
112tr_torrent*
113tr_torrentFindFromHash (tr_session * session, const uint8_t * torrentHash)
114{
115    tr_torrent * tor = NULL;
116
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;
121
122    return NULL;
123}
124
125tr_torrent*
126tr_torrentFindFromMagnetLink (tr_session * session, const char * magnet)
127{
128    tr_magnet_info * info;
129    tr_torrent * tor = NULL;
130
131    if ((info = tr_magnetParse (magnet)))
132    {
133        tor = tr_torrentFindFromHash (session, info->hash);
134        tr_magnetFree (info);
135    }
136
137    return tor;
138}
139
140tr_torrent*
141tr_torrentFindFromObfuscatedHash (tr_session * session,
142                                  const uint8_t * obfuscatedTorrentHash)
143{
144    tr_torrent * tor = NULL;
145
146    while ((tor = tr_torrentNext (session, tor)))
147        if (!memcmp (tor->obfuscatedHash, obfuscatedTorrentHash,
148                     SHA_DIGEST_LENGTH))
149            return tor;
150
151    return NULL;
152}
153
154bool
155tr_torrentIsPieceTransferAllowed (const tr_torrent  * tor,
156                                  tr_direction        direction)
157{
158    unsigned int limit;
159    bool allowed = true;
160
161    if (tr_torrentUsesSpeedLimit (tor, direction))
162        if (tr_torrentGetSpeedLimit_Bps (tor, direction) <= 0)
163            allowed = false;
164
165    if (tr_torrentUsesSessionLimits (tor))
166        if (tr_sessionGetActiveSpeedLimit_Bps (tor->session, direction, &limit))
167            if (limit <= 0)
168                allowed = false;
169
170    return allowed;
171}
172
173/***
174****  PER-TORRENT UL / DL SPEEDS
175***/
176
177void
178tr_torrentSetSpeedLimit_Bps (tr_torrent * tor, tr_direction dir, unsigned int Bps)
179{
180    assert (tr_isTorrent (tor));
181    assert (tr_isDirection (dir));
182
183    if (tr_bandwidthSetDesiredSpeed_Bps (&tor->bandwidth, dir, Bps))
184        tr_torrentSetDirty (tor);
185}
186void
187tr_torrentSetSpeedLimit_KBps (tr_torrent * tor, tr_direction dir, unsigned int KBps)
188{
189    tr_torrentSetSpeedLimit_Bps (tor, dir, toSpeedBytes (KBps));
190}
191
192unsigned int
193tr_torrentGetSpeedLimit_Bps (const tr_torrent * tor, tr_direction dir)
194{
195    assert (tr_isTorrent (tor));
196    assert (tr_isDirection (dir));
197
198    return tr_bandwidthGetDesiredSpeed_Bps (&tor->bandwidth, dir);
199}
200unsigned int
201tr_torrentGetSpeedLimit_KBps (const tr_torrent * tor, tr_direction dir)
202{
203    return toSpeedKBps (tr_torrentGetSpeedLimit_Bps (tor, dir));
204}
205
206void
207tr_torrentUseSpeedLimit (tr_torrent * tor, tr_direction dir, bool do_use)
208{
209    assert (tr_isTorrent (tor));
210    assert (tr_isDirection (dir));
211
212    if (tr_bandwidthSetLimited (&tor->bandwidth, dir, do_use))
213        tr_torrentSetDirty (tor);
214}
215
216bool
217tr_torrentUsesSpeedLimit (const tr_torrent * tor, tr_direction dir)
218{
219    assert (tr_isTorrent (tor));
220    assert (tr_isDirection (dir));
221
222    return tr_bandwidthIsLimited (&tor->bandwidth, dir);
223}
224
225void
226tr_torrentUseSessionLimits (tr_torrent * tor, bool doUse)
227{
228    bool changed;
229
230    assert (tr_isTorrent (tor));
231
232    changed = tr_bandwidthHonorParentLimits (&tor->bandwidth, TR_UP, doUse);
233    changed |= tr_bandwidthHonorParentLimits (&tor->bandwidth, TR_DOWN, doUse);
234
235    if (changed)
236        tr_torrentSetDirty (tor);
237}
238
239bool
240tr_torrentUsesSessionLimits (const tr_torrent * tor)
241{
242    assert (tr_isTorrent (tor));
243
244    return tr_bandwidthAreParentLimitsHonored (&tor->bandwidth, TR_UP);
245}
246
247/***
248****
249***/
250
251void
252tr_torrentSetRatioMode (tr_torrent *  tor, tr_ratiolimit mode)
253{
254    assert (tr_isTorrent (tor));
255    assert (mode==TR_RATIOLIMIT_GLOBAL || mode==TR_RATIOLIMIT_SINGLE || mode==TR_RATIOLIMIT_UNLIMITED);
256
257    if (mode != tor->ratioLimitMode)
258    {
259        tor->ratioLimitMode = mode;
260
261        tr_torrentSetDirty (tor);
262    }
263}
264
265tr_ratiolimit
266tr_torrentGetRatioMode (const tr_torrent * tor)
267{
268    assert (tr_isTorrent (tor));
269
270    return tor->ratioLimitMode;
271}
272
273void
274tr_torrentSetRatioLimit (tr_torrent * tor, double desiredRatio)
275{
276    assert (tr_isTorrent (tor));
277
278    if ((int)(desiredRatio*100.0) != (int)(tor->desiredRatio*100.0))
279    {
280        tor->desiredRatio = desiredRatio;
281
282        tr_torrentSetDirty (tor);
283    }
284}
285
286double
287tr_torrentGetRatioLimit (const tr_torrent * tor)
288{
289    assert (tr_isTorrent (tor));
290
291    return tor->desiredRatio;
292}
293
294bool
295tr_torrentGetSeedRatio (const tr_torrent * tor, double * ratio)
296{
297    bool isLimited;
298
299    switch (tr_torrentGetRatioMode (tor))
300    {
301        case TR_RATIOLIMIT_SINGLE:
302            isLimited = true;
303            if (ratio)
304                *ratio = tr_torrentGetRatioLimit (tor);
305            break;
306
307        case TR_RATIOLIMIT_GLOBAL:
308            isLimited = tr_sessionIsRatioLimited (tor->session);
309            if (isLimited && ratio)
310                *ratio = tr_sessionGetRatioLimit (tor->session);
311            break;
312
313        default: /* TR_RATIOLIMIT_UNLIMITED */
314            isLimited = false;
315            break;
316    }
317
318    return isLimited;
319}
320
321/* returns true if the seed ratio applies --
322 * it applies if the torrent's a seed AND it has a seed ratio set */
323static bool
324tr_torrentGetSeedRatioBytes (tr_torrent  * tor,
325                             uint64_t    * setmeLeft,
326                             uint64_t    * setmeGoal)
327{
328    double seedRatio;
329    bool seedRatioApplies = false;
330
331    if (tr_torrentGetSeedRatio (tor, &seedRatio))
332    {
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);
340    }
341
342    return seedRatioApplies;
343}
344
345static bool
346tr_torrentIsSeedRatioDone (tr_torrent * tor)
347{
348    uint64_t bytesLeft;
349    return tr_torrentGetSeedRatioBytes (tor, &bytesLeft, NULL) && !bytesLeft;
350}
351
352/***
353****
354***/
355
356void
357tr_torrentSetIdleMode (tr_torrent *  tor, tr_idlelimit mode)
358{
359    assert (tr_isTorrent (tor));
360    assert (mode==TR_IDLELIMIT_GLOBAL || mode==TR_IDLELIMIT_SINGLE || mode==TR_IDLELIMIT_UNLIMITED);
361
362    if (mode != tor->idleLimitMode)
363    {
364        tor->idleLimitMode = mode;
365
366        tr_torrentSetDirty (tor);
367    }
368}
369
370tr_idlelimit
371tr_torrentGetIdleMode (const tr_torrent * tor)
372{
373    assert (tr_isTorrent (tor));
374
375    return tor->idleLimitMode;
376}
377
378void
379tr_torrentSetIdleLimit (tr_torrent * tor, uint16_t idleMinutes)
380{
381    assert (tr_isTorrent (tor));
382
383    if (idleMinutes > 0)
384    {
385        tor->idleLimitMinutes = idleMinutes;
386
387        tr_torrentSetDirty (tor);
388    }
389}
390
391uint16_t
392tr_torrentGetIdleLimit (const tr_torrent * tor)
393{
394    assert (tr_isTorrent (tor));
395
396    return tor->idleLimitMinutes;
397}
398
399bool
400tr_torrentGetSeedIdle (const tr_torrent * tor, uint16_t * idleMinutes)
401{
402    bool isLimited;
403
404    switch (tr_torrentGetIdleMode (tor))
405    {
406        case TR_IDLELIMIT_SINGLE:
407            isLimited = true;
408            if (idleMinutes)
409                *idleMinutes = tr_torrentGetIdleLimit (tor);
410            break;
411
412        case TR_IDLELIMIT_GLOBAL:
413            isLimited = tr_sessionIsIdleLimited (tor->session);
414            if (isLimited && idleMinutes)
415                *idleMinutes = tr_sessionGetIdleLimit (tor->session);
416            break;
417
418        default: /* TR_IDLELIMIT_UNLIMITED */
419            isLimited = false;
420            break;
421    }
422
423    return isLimited;
424}
425
426static bool
427tr_torrentIsSeedIdleLimitDone (tr_torrent * tor)
428{
429    uint16_t idleMinutes;
430    return tr_torrentGetSeedIdle (tor, &idleMinutes)
431        && difftime (tr_time (), MAX (tor->startDate, tor->activityDate)) >= idleMinutes * 60u;
432}
433
434/***
435****
436***/
437
438void
439tr_torrentCheckSeedLimit (tr_torrent * tor)
440{
441    assert (tr_isTorrent (tor));
442
443    if (!tor->isRunning || tor->isStopping || !tr_torrentIsSeed (tor))
444        return;
445
446    /* if we're seeding and reach our seed ratio limit, stop the torrent */
447    if (tr_torrentIsSeedRatioDone (tor))
448    {
449        tr_torinf (tor, "Seed ratio reached; pausing torrent");
450
451        tor->isStopping = true;
452
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);
456    }
457    /* if we're seeding and reach our inactiviy limit, stop the torrent */
458    else if (tr_torrentIsSeedIdleLimitDone (tor))
459    {
460        tr_torinf (tor, "Seeding idle limit reached; pausing torrent");
461
462        tor->isStopping = true;
463        tor->finishedSeedingByIdle = true;
464
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);
468    }
469}
470
471/***
472****
473***/
474
475void
476tr_torrentSetLocalError (tr_torrent * tor, const char * fmt, ...)
477{
478    va_list ap;
479
480    assert (tr_isTorrent (tor));
481
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);
487
488    tr_torerr (tor, "%s", tor->errorString);
489
490    if (tor->isRunning)
491        tor->isStopping = true;
492}
493
494static void
495tr_torrentClearError (tr_torrent * tor)
496{
497    tor->error = TR_STAT_OK;
498    tor->errorString[0] = '\0';
499    tor->errorTracker[0] = '\0';
500}
501
502static void
503onTrackerResponse (tr_torrent * tor, const tr_tracker_event * event, void * unused UNUSED)
504{
505    switch (event->messageType)
506    {
507        case TR_TRACKER_PEERS:
508        {
509            size_t i;
510            const int8_t seedProbability = event->seedProbability;
511            const bool allAreSeeds = seedProbability == 100;
512
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);
517
518            for (i = 0; i < event->pexCount; ++i)
519                tr_peerMgrAddPex (tor, TR_PEER_FROM_TRACKER, &event->pex[i], seedProbability);
520
521            break;
522        }
523
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;
530
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;
537
538        case TR_TRACKER_ERROR_CLEAR:
539            if (tor->error != TR_STAT_LOCAL_ERROR)
540                tr_torrentClearError (tor);
541            break;
542    }
543}
544
545/***
546****
547****  TORRENT INSTANTIATION
548****
549***/
550
551static tr_piece_index_t
552getBytePiece (const tr_info * info, uint64_t byteOffset)
553{
554    tr_piece_index_t piece;
555
556    assert (info);
557    assert (info->pieceSize != 0);
558
559    piece = byteOffset / info->pieceSize;
560
561    /* handle 0-byte files at the end of a torrent */
562    if (byteOffset == info->totalSize)
563      piece = info->pieceCount - 1;
564
565    return piece;
566}
567
568static void
569initFilePieces (tr_info *       info,
570                tr_file_index_t fileIndex)
571{
572    tr_file * file;
573    uint64_t  firstByte, lastByte;
574
575    assert (info);
576    assert (fileIndex < info->fileCount);
577
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);
583}
584
585static int
586pieceHasFile (tr_piece_index_t piece,
587              const tr_file *  file)
588{
589    return (file->firstPiece <= piece) && (piece <= file->lastPiece);
590}
591
592static tr_priority_t
593calculatePiecePriority (const tr_torrent * tor,
594                        tr_piece_index_t   piece,
595                        int                fileHint)
596{
597    tr_file_index_t i;
598    tr_priority_t priority = TR_PRI_LOW;
599
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;
609    }
610
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)
614    {
615        const tr_file * file = &tor->info.files[i];
616
617        if (!pieceHasFile (piece, file))
618            break;
619
620        priority = MAX (priority, file->priority);
621
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;
628    }
629
630    return priority;
631}
632
633static void
634tr_torrentInitFilePieces (tr_torrent * tor)
635{
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;
641
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);
647    }
648
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;
655    }
656
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]);
669    }
670#endif
671
672    for (p=0; p<inf->pieceCount; ++p)
673        inf->pieces[p].priority = calculatePiecePriority (tor, p, firstFiles[p]);
674
675    tr_free (firstFiles);
676}
677
678static void torrentStart (tr_torrent * tor, bool bypass_queue);
679
680/**
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
684 */
685uint32_t
686tr_getBlockSize (uint32_t pieceSize)
687{
688    uint32_t b = pieceSize;
689
690    while (b > MAX_BLOCK_SIZE)
691        b /= 2u;
692
693    if (!b || (pieceSize % b)) /* not cleanly divisible */
694        return 0;
695    return b;
696}
697
698static void refreshCurrentDir (tr_torrent * tor);
699
700static void
701torrentInitFromInfo (tr_torrent * tor)
702{
703    uint64_t t;
704    tr_info * info = &tor->info;
705
706    tor->blockSize = tr_getBlockSize (info->pieceSize);
707
708    if (info->pieceSize)
709        tor->lastPieceSize = (uint32_t)(info->totalSize % info->pieceSize);
710
711    if (!tor->lastPieceSize)
712        tor->lastPieceSize = info->pieceSize;
713
714    if (tor->blockSize)
715        tor->lastBlockSize = info->totalSize % tor->blockSize;
716
717    if (!tor->lastBlockSize)
718        tor->lastBlockSize = tor->blockSize;
719
720    tor->blockCount = tor->blockSize
721        ? (info->totalSize + tor->blockSize - 1) / tor->blockSize
722        : 0;
723
724    tor->blockCountInPiece = tor->blockSize
725        ? info->pieceSize / tor->blockSize
726        : 0;
727
728    tor->blockCountInLastPiece = tor->blockSize
729        ? (tor->lastPieceSize + tor->blockSize - 1) / tor->blockSize
730        : 0;
731
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);
747
748    tr_cpConstruct (&tor->completion, tor);
749
750    tr_torrentInitFilePieces (tor);
751
752    tor->completeness = tr_cpGetStatus (&tor->completion);
753}
754
755static void tr_torrentFireMetadataCompleted (tr_torrent * tor);
756
757void
758tr_torrentGotNewInfoDict (tr_torrent * tor)
759{
760    torrentInitFromInfo (tor);
761
762    tr_peerMgrOnTorrentGotMetainfo (tor);
763
764    tr_torrentFireMetadataCompleted (tor);
765}
766
767static bool
768hasAnyLocalData (const tr_torrent * tor)
769{
770    tr_file_index_t i;
771
772    for (i=0; i<tor->info.fileCount; ++i)
773        if (tr_torrentFindFile2 (tor, i, NULL, NULL, NULL))
774            return true;
775
776    return false;
777}
778
779static bool
780setLocalErrorIfFilesDisappeared (tr_torrent * tor)
781{
782    const bool disappeared = (tr_cpHaveTotal (&tor->completion) > 0) && !hasAnyLocalData (tor);
783
784    if (disappeared)
785    {
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."));
788    }
789
790    return disappeared;
791}
792
793static void
794torrentInit (tr_torrent * tor, const tr_ctor * ctor)
795{
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);
803
804    assert (session != NULL);
805
806    tr_sessionLock (session);
807
808    tor->session   = session;
809    tor->uniqueId = nextUniqueId++;
810    tor->magicNumber = TORRENT_MAGIC_NUMBER;
811    tor->queuePosition = session->torrentCount;
812
813    tr_peerIdInit (tor->peer_id);
814
815    tr_sha1 (tor->obfuscatedHash, "req2", 4,
816             tor->info.hash, SHA_DIGEST_LENGTH,
817             NULL);
818
819    if (!tr_ctorGetDownloadDir (ctor, TR_FORCE, &dir) ||
820        !tr_ctorGetDownloadDir (ctor, TR_FALLBACK, &dir))
821            tor->downloadDir = tr_strdup (dir);
822
823    if (tr_ctorGetIncompleteDir (ctor, &dir))
824        dir = tr_sessionGetIncompleteDir (session);
825    if (tr_sessionIsIncompleteDirEnabled (session))
826        tor->incompleteDir = tr_strdup (dir);
827
828    tr_bandwidthConstruct (&tor->bandwidth, session, &session->bandwidth);
829
830    tor->bandwidth.priority = tr_ctorGetBandwidthPriority (ctor);
831
832    tor->error = TR_STAT_OK;
833
834    tor->finishedSeedingByIdle = false;
835
836    tr_peerMgrAddTorrent (session->peerMgr, tor);
837
838    assert (!tor->downloadedCur);
839    assert (!tor->uploadedCur);
840
841    tr_torrentSetAddedDate (tor, tr_time ()); /* this is a default value to be
842                                                  overwritten by the resume file */
843
844    torrentInitFromInfo (tor);
845    loaded = tr_torrentLoadResume (tor, ~0, ctor);
846    tor->completeness = tr_cpGetStatus (&tor->completion);
847    setLocalErrorIfFilesDisappeared (tor);
848
849    tr_ctorInitTorrentPriorities (ctor, tor);
850    tr_ctorInitTorrentWanted (ctor, tor);
851
852    refreshCurrentDir (tor);
853
854    doStart = tor->isRunning;
855    tor->isRunning = 0;
856
857    if (! (loaded & TR_FR_SPEEDLIMIT))
858    {
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);
864    }
865
866    if (! (loaded & TR_FR_RATIOLIMIT))
867    {
868        tr_torrentSetRatioMode (tor, TR_RATIOLIMIT_GLOBAL);
869        tr_torrentSetRatioLimit (tor, tr_sessionGetRatioLimit (tor->session));
870    }
871
872    if (! (loaded & TR_FR_IDLELIMIT))
873    {
874        tr_torrentSetIdleMode (tor, TR_IDLELIMIT_GLOBAL);
875        tr_torrentSetIdleLimit (tor, tr_sessionGetIdleLimit (tor->session));
876    }
877
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;
887    }
888
889    /* if we don't have a local .torrent file already, assume the torrent is new */
890    isNewTorrent = stat (tor->info.torrent, &st);
891
892    /* maybe save our own copy of the metainfo */
893    if (tr_ctorGetSave (ctor))
894    {
895        const tr_benc * val;
896        if (!tr_ctorGetMetainfo (ctor, &val))
897        {
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);
903        }
904    }
905
906    tor->tiers = tr_announcerAddTorrent (tor, onTrackerResponse, NULL);
907
908    if (isNewTorrent)
909    {
910        tor->startAfterVerify = doStart;
911        tr_torrentVerify (tor);
912    }
913    else if (doStart)
914    {
915        tr_torrentStart (tor);
916    }
917
918    tr_sessionUnlock (session);
919}
920
921static tr_parse_result
922torrentParseImpl (const tr_ctor * ctor, tr_info * setmeInfo,
923                  bool * setmeHasInfo, int * dictLength)
924{
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;
932
933    if (setmeInfo == NULL)
934        setmeInfo = &tmp;
935    memset (setmeInfo, 0, sizeof (tr_info));
936
937    if (tr_ctorGetMetainfo (ctor, &metainfo))
938        return TR_PARSE_ERR;
939
940    didParse = tr_metainfoParse (session, metainfo, setmeInfo,
941                                 &hasInfo, dictLength);
942    doFree = didParse && (setmeInfo == &tmp);
943
944    if (!didParse)
945        result = TR_PARSE_ERR;
946
947    if (didParse && hasInfo && !tr_getBlockSize (setmeInfo->pieceSize))
948        result = TR_PARSE_ERR;
949
950    if (didParse && session && tr_torrentExists (session, setmeInfo->hash))
951        result = TR_PARSE_DUPLICATE;
952
953    if (doFree)
954        tr_metainfoFree (setmeInfo);
955
956    if (setmeHasInfo != NULL)
957        *setmeHasInfo = hasInfo;
958
959    return result;
960}
961
962tr_parse_result
963tr_torrentParse (const tr_ctor * ctor, tr_info * setmeInfo)
964{
965    return torrentParseImpl (ctor, setmeInfo, NULL, NULL);
966}
967
968tr_torrent *
969tr_torrentNew (const tr_ctor * ctor, int * setmeError)
970{
971    int len;
972    bool hasInfo;
973    tr_info tmpInfo;
974    tr_parse_result r;
975    tr_torrent * tor = NULL;
976
977    assert (ctor != NULL);
978    assert (tr_isSession (tr_ctorGetSession (ctor)));
979
980    r = torrentParseImpl (ctor, &tmpInfo, &hasInfo, &len);
981    if (r == TR_PARSE_OK)
982    {
983        tor = tr_new0 (tr_torrent, 1);
984        tor->info = tmpInfo;
985        if (hasInfo)
986            tor->infoDictLength = len;
987        torrentInit (tor, ctor);
988    }
989    else
990    {
991        if (r == TR_PARSE_DUPLICATE)
992            tr_metainfoFree (&tmpInfo);
993
994        if (setmeError)
995            *setmeError = r;
996    }
997
998    return tor;
999}
1000
1001/**
1002***
1003**/
1004
1005void
1006tr_torrentSetDownloadDir (tr_torrent * tor, const char * path)
1007{
1008    assert (tr_isTorrent (tor));
1009
1010    if (!path || !tor->downloadDir || strcmp (path, tor->downloadDir))
1011    {
1012        tr_free (tor->downloadDir);
1013        tor->downloadDir = tr_strdup (path);
1014        tr_torrentSetDirty (tor);
1015    }
1016
1017    refreshCurrentDir (tor);
1018}
1019
1020const char*
1021tr_torrentGetDownloadDir (const tr_torrent * tor)
1022{
1023    assert (tr_isTorrent (tor));
1024
1025    return tor->downloadDir;
1026}
1027
1028const char *
1029tr_torrentGetCurrentDir (const tr_torrent * tor)
1030{
1031    assert (tr_isTorrent (tor));
1032
1033    return tor->currentDir;
1034}
1035
1036
1037void
1038tr_torrentChangeMyPort (tr_torrent * tor)
1039{
1040    assert (tr_isTorrent (tor));
1041
1042    if (tor->isRunning)
1043        tr_announcerChangeMyPort (tor);
1044}
1045
1046static inline void
1047tr_torrentManualUpdateImpl (void * vtor)
1048{
1049    tr_torrent * tor = vtor;
1050
1051    assert (tr_isTorrent (tor));
1052
1053    if (tor->isRunning)
1054        tr_announcerManualAnnounce (tor);
1055}
1056
1057void
1058tr_torrentManualUpdate (tr_torrent * tor)
1059{
1060    assert (tr_isTorrent (tor));
1061
1062    tr_runInEventThread (tor->session, tr_torrentManualUpdateImpl, tor);
1063}
1064
1065bool
1066tr_torrentCanManualUpdate (const tr_torrent * tor)
1067{
1068    return (tr_isTorrent (tor))
1069        && (tor->isRunning)
1070        && (tr_announcerCanManualAnnounce (tor));
1071}
1072
1073const tr_info *
1074tr_torrentInfo (const tr_torrent * tor)
1075{
1076    return tr_isTorrent (tor) ? &tor->info : NULL;
1077}
1078
1079const tr_stat *
1080tr_torrentStatCached (tr_torrent * tor)
1081{
1082    const time_t now = tr_time ();
1083
1084    return tr_isTorrent (tor) && (now == tor->lastStatTime)
1085         ? &tor->stats
1086         : tr_torrentStat (tor);
1087}
1088
1089void
1090tr_torrentSetVerifyState (tr_torrent * tor, tr_verify_state state)
1091{
1092    assert (tr_isTorrent (tor));
1093    assert (state==TR_VERIFY_NONE || state==TR_VERIFY_WAIT || state==TR_VERIFY_NOW);
1094
1095    tor->verifyState = state;
1096    tor->anyDate = tr_time ();
1097}
1098
1099static tr_torrent_activity
1100torrentGetActivity (const tr_torrent * tor)
1101{
1102    const bool is_seed = tr_torrentIsSeed (tor);
1103    assert (tr_isTorrent (tor));
1104
1105    if (tor->verifyState == TR_VERIFY_NOW)
1106        return TR_STATUS_CHECK;
1107
1108    if (tor->verifyState == TR_VERIFY_WAIT)
1109        return TR_STATUS_CHECK_WAIT;
1110
1111    if (tor->isRunning)
1112        return is_seed ? TR_STATUS_SEED : TR_STATUS_DOWNLOAD;
1113
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;
1119    }
1120
1121    return TR_STATUS_STOPPED;
1122}
1123
1124tr_torrent_activity
1125tr_torrentGetActivity (tr_torrent * tor)
1126{
1127    /* FIXME: is this call still needed? */
1128    tr_torrentRecheckCompleteness (tor);
1129
1130    return torrentGetActivity (tor);
1131}
1132
1133static time_t
1134torrentGetIdleSecs (const tr_torrent * tor)
1135{
1136    int idle_secs;
1137    const tr_torrent_activity activity = torrentGetActivity (tor);
1138
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;
1143
1144    return idle_secs;
1145}
1146
1147bool
1148tr_torrentIsStalled (const tr_torrent * tor)
1149{
1150    return tr_sessionGetQueueStalledEnabled (tor->session)
1151        && (torrentGetIdleSecs (tor) > (tr_sessionGetQueueStalledMinutes (tor->session) * 60));
1152}
1153
1154
1155static double
1156getVerifyProgress (const tr_torrent * tor)
1157{
1158    double d = 0;
1159
1160    assert (tr_isTorrent (tor));
1161
1162    if (tr_torrentHasMetadata (tor))
1163    {
1164        tr_piece_index_t i, n;
1165        tr_piece_index_t checked = 0;
1166
1167        for (i=0, n=tor->info.pieceCount; i!=n; ++i)
1168            if (tor->info.pieces[i].timeChecked)
1169                ++checked;
1170
1171        d = checked / (double)tor->info.pieceCount;
1172    }
1173
1174    return d;
1175}
1176
1177const tr_stat *
1178tr_torrentStat (tr_torrent * tor)
1179{
1180    tr_stat *               s;
1181    uint64_t                now;
1182    uint64_t                seedRatioBytesLeft;
1183    uint64_t                seedRatioBytesGoal;
1184    bool                    seedRatioApplies;
1185    uint16_t                seedIdleMinutes;
1186
1187    if (!tor)
1188        return NULL;
1189
1190    assert (tr_isTorrent (tor));
1191    tr_torrentLock (tor);
1192
1193    tor->lastStatTime = tr_time ();
1194
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));
1202
1203    s->manualAnnounceTime = tr_announcerNextManualAnnounce (tor);
1204
1205    tr_peerMgrTorrentStats (tor,
1206                            &s->peersConnected,
1207                            &s->webseedsSendingToUs,
1208                            &s->peersSendingToUs,
1209                            &s->peersGettingFromUs,
1210                            s->peersFrom);
1211
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));
1217
1218    s->percentComplete = tr_cpPercentComplete (&tor->completion);
1219    s->metadataPercentComplete = tr_torrentGetMetadataPercent (tor);
1220
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);
1232
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);
1239
1240    s->ratio = tr_getRatio (s->uploadedEver,
1241                            s->downloadedEver ? s->downloadedEver : s->haveValid);
1242
1243    seedRatioApplies = tr_torrentGetSeedRatioBytes (tor, &seedRatioBytesLeft,
1244                                                         &seedRatioBytesGoal);
1245
1246    switch (s->activity)
1247    {
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 */
1252
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;
1259            }
1260
1261            if (s->leftUntilDone > s->desiredAvailable)
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);
1267
1268            s->etaIdle = TR_ETA_NOT_AVAIL;
1269            break;
1270
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;
1280                }
1281                if (tor->etaULSpeed_KBps < 1)
1282                    s->eta = TR_ETA_UNKNOWN;
1283                else
1284                    s->eta = seedRatioBytesLeft / toSpeedBytes (tor->etaULSpeed_KBps);
1285            }
1286
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;
1292        }
1293
1294        default:
1295            s->eta = TR_ETA_NOT_AVAIL;
1296            s->etaIdle = TR_ETA_NOT_AVAIL;
1297            break;
1298    }
1299
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);
1303
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;
1310
1311    tr_torrentUnlock (tor);
1312
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;
1318}
1319
1320/***
1321****
1322***/
1323
1324static uint64_t
1325fileBytesCompleted (const tr_torrent * tor, tr_file_index_t index)
1326{
1327    uint64_t total = 0;
1328    const tr_file * f = &tor->info.files[index];
1329
1330    if (f->length)
1331    {
1332        tr_block_index_t first;
1333        tr_block_index_t last;
1334        tr_torGetFileBlockRange (tor, index, &first, &last);
1335
1336        if (first == last)
1337        {
1338            if (tr_cpBlockIsComplete (&tor->completion, first))
1339                total = f->length;
1340        }
1341        else
1342        {
1343            /* the first block */
1344            if (tr_cpBlockIsComplete (&tor->completion, first))
1345                total += tor->blockSize - (f->offset % tor->blockSize);
1346
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;
1352            }
1353
1354            /* the last block */
1355            if (tr_cpBlockIsComplete (&tor->completion, last))
1356                total += (f->offset + f->length) - ((uint64_t)tor->blockSize * last);
1357        }
1358    }
1359
1360    return total;
1361}
1362
1363tr_file_stat *
1364tr_torrentFiles (const tr_torrent * tor,
1365                 tr_file_index_t *  fileCount)
1366{
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;
1372
1373    assert (tr_isTorrent (tor));
1374
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;
1379    }
1380
1381    if (fileCount)
1382        *fileCount = n;
1383
1384    return files;
1385}
1386
1387void
1388tr_torrentFilesFree (tr_file_stat *            files,
1389                     tr_file_index_t fileCount UNUSED)
1390{
1391    tr_free (files);
1392}
1393
1394/***
1395****
1396***/
1397
1398double*
1399tr_torrentWebSpeeds_KBps (const tr_torrent * tor)
1400{
1401    double * ret = NULL;
1402
1403    if (tr_isTorrent (tor))
1404    {
1405        tr_torrentLock (tor);
1406        ret = tr_peerMgrWebSpeeds_KBps (tor);
1407        tr_torrentUnlock (tor);
1408    }
1409
1410    return ret;
1411}
1412
1413tr_peer_stat *
1414tr_torrentPeers (const tr_torrent * tor, int * peerCount)
1415{
1416    tr_peer_stat * ret = NULL;
1417
1418    if (tr_isTorrent (tor))
1419    {
1420        tr_torrentLock (tor);
1421        ret = tr_peerMgrPeerStats (tor, peerCount);
1422        tr_torrentUnlock (tor);
1423    }
1424
1425    return ret;
1426}
1427
1428void
1429tr_torrentPeersFree (tr_peer_stat * peers, int peerCount UNUSED)
1430{
1431    tr_free (peers);
1432}
1433
1434tr_tracker_stat *
1435tr_torrentTrackers (const tr_torrent * torrent, int * setmeTrackerCount)
1436{
1437    tr_tracker_stat * ret = NULL;
1438
1439    if (tr_isTorrent (torrent))
1440    {
1441        tr_torrentLock (torrent);
1442        ret = tr_announcerStats (torrent, setmeTrackerCount);
1443        tr_torrentUnlock (torrent);
1444    }
1445
1446    return ret;
1447}
1448
1449void
1450tr_torrentTrackersFree (tr_tracker_stat * trackers, int trackerCount)
1451{
1452    tr_announcerStatsFree (trackers, trackerCount);
1453}
1454
1455void
1456tr_torrentAvailability (const tr_torrent * tor, int8_t * tab, int size)
1457{
1458    if (tr_isTorrent (tor) && (tab != NULL) && (size > 0))
1459    {
1460        tr_torrentLock (tor);
1461        tr_peerMgrTorrentAvailability (tor, tab, size);
1462        tr_torrentUnlock (tor);
1463    }
1464}
1465
1466void
1467tr_torrentAmountFinished (const tr_torrent * tor,
1468                          float *            tab,
1469                          int                size)
1470{
1471    assert (tr_isTorrent (tor));
1472
1473    tr_torrentLock (tor);
1474    tr_cpGetAmountDone (&tor->completion, tab, size);
1475    tr_torrentUnlock (tor);
1476}
1477
1478static void
1479tr_torrentResetTransferStats (tr_torrent * tor)
1480{
1481    tr_torrentLock (tor);
1482
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;
1489
1490    tr_torrentSetDirty (tor);
1491
1492    tr_torrentUnlock (tor);
1493}
1494
1495void
1496tr_torrentSetHasPiece (tr_torrent *     tor,
1497                       tr_piece_index_t pieceIndex,
1498                       bool             has)
1499{
1500    assert (tr_isTorrent (tor));
1501    assert (pieceIndex < tor->info.pieceCount);
1502
1503    if (has)
1504        tr_cpPieceAdd (&tor->completion, pieceIndex);
1505    else
1506        tr_cpPieceRem (&tor->completion, pieceIndex);
1507}
1508
1509/***
1510****
1511***/
1512
1513#ifndef NDEBUG
1514static bool queueIsSequenced (tr_session *);
1515#endif
1516
1517static void
1518freeTorrent (tr_torrent * tor)
1519{
1520    tr_torrent * t;
1521    tr_session *  session = tor->session;
1522    tr_info *    inf = &tor->info;
1523    const time_t now = tr_time ();
1524
1525    assert (!tor->isRunning);
1526
1527    tr_sessionLock (session);
1528
1529    tr_peerMgrRemoveTorrent (tor);
1530
1531    tr_announcerRemoveTorrent (session->announcer, tor);
1532
1533    tr_cpDestruct (&tor->completion);
1534
1535    tr_free (tor->downloadDir);
1536    tr_free (tor->incompleteDir);
1537
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;
1544        }
1545    }
1546
1547    /* decrement the torrent count */
1548    assert (session->torrentCount >= 1);
1549    session->torrentCount--;
1550
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;
1557        }
1558    }
1559    assert (queueIsSequenced (session));
1560
1561    tr_bandwidthDestruct (&tor->bandwidth);
1562
1563    tr_metainfoFree (inf);
1564    memset (tor, ~0, sizeof (tr_torrent));
1565    tr_free (tor);
1566
1567    tr_sessionUnlock (session);
1568}
1569
1570/**
1571***  Start/Stop Callback
1572**/
1573
1574static void torrentSetQueued (tr_torrent * tor, bool queued);
1575
1576static void
1577torrentStartImpl (void * vtor)
1578{
1579    time_t now;
1580    tr_torrent * tor = vtor;
1581
1582    assert (tr_isTorrent (tor));
1583
1584    tr_sessionLock (tor->session);
1585
1586    tr_torrentRecheckCompleteness (tor);
1587    torrentSetQueued (tor, false);
1588
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;
1595
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);
1602
1603    tr_sessionUnlock (tor->session);
1604}
1605
1606uint64_t
1607tr_torrentGetCurrentSizeOnDisk (const tr_torrent * tor)
1608{
1609    tr_file_index_t i;
1610    uint64_t byte_count = 0;
1611    const tr_file_index_t n = tor->info.fileCount;
1612
1613    for (i=0; i<n; ++i)
1614    {
1615        struct stat sb;
1616        char * filename = tr_torrentFindFile (tor, i);
1617
1618        sb.st_size = 0;
1619        if (filename && !stat (filename, &sb))
1620            byte_count += sb.st_size;
1621
1622        tr_free (filename);
1623    }
1624
1625    return byte_count;
1626}
1627
1628static bool
1629torrentShouldQueue (const tr_torrent * tor)
1630{
1631    const tr_direction dir = tr_torrentGetQueueDirection (tor);
1632
1633    return tr_sessionCountQueueFreeSlots (tor->session, dir) == 0;
1634}
1635
1636static void
1637torrentStart (tr_torrent * tor, bool bypass_queue)
1638{
1639    switch (torrentGetActivity (tor))
1640    {
1641        case TR_STATUS_SEED:
1642        case TR_STATUS_DOWNLOAD:
1643            return; /* already started */
1644            break;
1645
1646        case TR_STATUS_SEED_WAIT:
1647        case TR_STATUS_DOWNLOAD_WAIT:
1648            if (!bypass_queue)
1649                return; /* already queued */
1650            break;
1651
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;
1659
1660        case TR_STATUS_STOPPED:
1661            if (!bypass_queue && torrentShouldQueue (tor)) {
1662                torrentSetQueued (tor, true);
1663                return;
1664            }
1665            break;
1666    }
1667
1668    /* don't allow the torrent to be started if the files disappeared */
1669    if (setLocalErrorIfFilesDisappeared (tor))
1670        return;
1671
1672    /* otherwise, start it now... */
1673    tr_sessionLock (tor->session);
1674
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);
1679    }
1680
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);
1690
1691    tr_sessionUnlock (tor->session);
1692}
1693
1694void
1695tr_torrentStart (tr_torrent * tor)
1696{
1697    if (tr_isTorrent (tor))
1698        torrentStart (tor, false);
1699}
1700
1701void
1702tr_torrentStartNow (tr_torrent * tor)
1703{
1704    if (tr_isTorrent (tor))
1705        torrentStart (tor, true);
1706}
1707
1708static void
1709torrentRecheckDoneImpl (void * vtor)
1710{
1711    tr_torrent * tor = vtor;
1712    assert (tr_isTorrent (tor));
1713
1714    tr_torrentRecheckCompleteness (tor);
1715
1716    if (tor->startAfterVerify) {
1717        tor->startAfterVerify = false;
1718        torrentStart (tor, false);
1719    }
1720}
1721
1722static void
1723torrentRecheckDoneCB (tr_torrent * tor)
1724{
1725    assert (tr_isTorrent (tor));
1726
1727    tr_runInEventThread (tor->session, torrentRecheckDoneImpl, tor);
1728}
1729
1730static void
1731verifyTorrent (void * vtor)
1732{
1733    bool startAfter;
1734    tr_torrent * tor = vtor;
1735
1736    tr_sessionLock (tor->session);
1737
1738    /* if the torrent's already being verified, stop it */
1739    tr_verifyRemove (tor);
1740
1741    startAfter = (tor->isRunning || tor->startAfterVerify) && !tor->isStopping;
1742
1743    if (tor->isRunning)
1744        tr_torrentStop (tor);
1745
1746    tor->startAfterVerify = startAfter;
1747
1748    if (setLocalErrorIfFilesDisappeared (tor))
1749        tor->startAfterVerify = false;
1750    else
1751        tr_verifyAdd (tor, torrentRecheckDoneCB);
1752
1753    tr_sessionUnlock (tor->session);
1754}
1755
1756void
1757tr_torrentVerify (tr_torrent * tor)
1758{
1759    if (tr_isTorrent (tor))
1760        tr_runInEventThread (tor->session, verifyTorrent, tor);
1761}
1762
1763void
1764tr_torrentSave (tr_torrent * tor)
1765{
1766    assert (tr_isTorrent (tor));
1767
1768    if (tor->isDirty)
1769    {
1770        tor->isDirty = false;
1771        tr_torrentSaveResume (tor);
1772    }
1773}
1774
1775static void
1776stopTorrent (void * vtor)
1777{
1778    tr_torrent * tor = vtor;
1779    tr_torinf (tor, "Pausing");
1780
1781    assert (tr_isTorrent (tor));
1782
1783    tr_torrentLock (tor);
1784
1785    tr_verifyRemove (tor);
1786    torrentSetQueued (tor, false);
1787    tr_peerMgrStopTorrent (tor);
1788    tr_announcerTorrentStopped (tor);
1789    tr_cacheFlushTorrent (tor->session->cache, tor);
1790
1791    tr_fdTorrentClose (tor->session, tor->uniqueId);
1792
1793    if (!tor->isDeleting)
1794        tr_torrentSave (tor);
1795
1796    tr_torrentUnlock (tor);
1797}
1798
1799void
1800tr_torrentStop (tr_torrent * tor)
1801{
1802    assert (tr_isTorrent (tor));
1803
1804    if (tr_isTorrent (tor))
1805    {
1806        tr_sessionLock (tor->session);
1807
1808        tor->isRunning = 0;
1809        tor->isStopping = 0;
1810        tr_torrentSetDirty (tor);
1811        tr_runInEventThread (tor->session, stopTorrent, tor);
1812
1813        tr_sessionUnlock (tor->session);
1814    }
1815}
1816
1817static void
1818closeTorrent (void * vtor)
1819{
1820    tr_benc * d;
1821    tr_torrent * tor = vtor;
1822
1823    assert (tr_isTorrent (tor));
1824
1825    d = tr_bencListAddDict (&tor->session->removedTorrents, 2);
1826    tr_bencDictAddInt (d, "id", tor->uniqueId);
1827    tr_bencDictAddInt (d, "date", tr_time ());
1828
1829    tr_torinf (tor, "%s", _ ("Removing torrent"));
1830
1831    stopTorrent (tor);
1832
1833    if (tor->isDeleting)
1834    {
1835        tr_metainfoRemoveSaved (tor->session, &tor->info);
1836        tr_torrentRemoveResume (tor);
1837    }
1838
1839    tor->isRunning = 0;
1840    freeTorrent (tor);
1841}
1842
1843void
1844tr_torrentFree (tr_torrent * tor)
1845{
1846    if (tr_isTorrent (tor))
1847    {
1848        tr_session * session = tor->session;
1849        assert (tr_isSession (session));
1850        tr_sessionLock (session);
1851
1852        tr_torrentClearCompletenessCallback (tor);
1853        tr_runInEventThread (session, closeTorrent, tor);
1854
1855        tr_sessionUnlock (session);
1856    }
1857}
1858
1859struct remove_data
1860{
1861    tr_torrent   * tor;
1862    bool           deleteFlag;
1863    tr_fileFunc  * deleteFunc;
1864};
1865
1866static void tr_torrentDeleteLocalData (tr_torrent *, tr_fileFunc);
1867
1868static void
1869removeTorrent (void * vdata)
1870{
1871    struct remove_data * data = vdata;
1872
1873    if (data->deleteFlag)
1874        tr_torrentDeleteLocalData (data->tor, data->deleteFunc);
1875
1876    tr_torrentClearCompletenessCallback (data->tor);
1877    closeTorrent (data->tor);
1878    tr_free (data);
1879}
1880
1881void
1882tr_torrentRemove (tr_torrent   * tor,
1883                  bool           deleteFlag,
1884                  tr_fileFunc    deleteFunc)
1885{
1886    struct remove_data * data;
1887
1888    assert (tr_isTorrent (tor));
1889    tor->isDeleting = 1;
1890
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);
1896}
1897
1898/**
1899***  Completeness
1900**/
1901
1902static const char *
1903getCompletionString (int type)
1904{
1905    switch (type)
1906    {
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");
1914
1915        case TR_SEED:
1916            return _ ("Complete");
1917
1918        default:
1919            return _ ("Incomplete");
1920    }
1921}
1922
1923static void
1924fireCompletenessChange (tr_torrent       * tor,
1925                        tr_completeness    status,
1926                        bool               wasRunning)
1927{
1928    assert ((status == TR_LEECH)
1929         || (status == TR_SEED)
1930         || (status == TR_PARTIAL_SEED));
1931
1932    if (tor->completeness_func)
1933        tor->completeness_func (tor, status, wasRunning,
1934                                tor->completeness_func_user_data);
1935}
1936
1937void
1938tr_torrentSetCompletenessCallback (tr_torrent                    * tor,
1939                                   tr_torrent_completeness_func    func,
1940                                   void                          * user_data)
1941{
1942    assert (tr_isTorrent (tor));
1943
1944    tor->completeness_func = func;
1945    tor->completeness_func_user_data = user_data;
1946}
1947
1948void
1949tr_torrentClearCompletenessCallback (tr_torrent * torrent)
1950{
1951    tr_torrentSetCompletenessCallback (torrent, NULL, NULL);
1952}
1953
1954void
1955tr_torrentSetRatioLimitHitCallback (tr_torrent                     * tor,
1956                                    tr_torrent_ratio_limit_hit_func  func,
1957                                    void                           * user_data)
1958{
1959    assert (tr_isTorrent (tor));
1960
1961    tor->ratio_limit_hit_func = func;
1962    tor->ratio_limit_hit_func_user_data = user_data;
1963}
1964
1965void
1966tr_torrentClearRatioLimitHitCallback (tr_torrent * torrent)
1967{
1968    tr_torrentSetRatioLimitHitCallback (torrent, NULL, NULL);
1969}
1970
1971void
1972tr_torrentSetIdleLimitHitCallback (tr_torrent                    * tor,
1973                                   tr_torrent_idle_limit_hit_func  func,
1974                                   void                          * user_data)
1975{
1976    assert (tr_isTorrent (tor));
1977
1978    tor->idle_limit_hit_func = func;
1979    tor->idle_limit_hit_func_user_data = user_data;
1980}
1981
1982void
1983tr_torrentClearIdleLimitHitCallback (tr_torrent * torrent)
1984{
1985    tr_torrentSetIdleLimitHitCallback (torrent, NULL, NULL);
1986}
1987
1988static void
1989onSigCHLD (int i UNUSED)
1990{
1991    waitpid (-1, NULL, WNOHANG);
1992}
1993
1994static void
1995torrentCallScript (const tr_torrent * tor, const char * script)
1996{
1997    char timeStr[128];
1998    const time_t now = tr_time ();
1999
2000    tr_strlcpy (timeStr, ctime (&now), sizeof (timeStr));
2001    *strchr (timeStr,'\n') = '\0';
2002
2003    if (script && *script)
2004    {
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 };
2015
2016        tr_torinf (tor, "Calling script \"%s\"", script);
2017
2018#ifdef WIN32
2019        _spawnvpe (_P_NOWAIT, script, (const char*)cmd, env);
2020#else
2021        signal (SIGCHLD, onSigCHLD);
2022
2023        if (!fork ())
2024        {
2025            for (i=0; env[i]; ++i)
2026                putenv (env[i]);
2027            execvp (script, cmd);
2028            _exit (0);
2029        }
2030#endif
2031
2032        for (i=0; cmd[i]; ++i) tr_free (cmd[i]);
2033        for (i=0; env[i]; ++i) tr_free (env[i]);
2034    }
2035}
2036
2037void
2038tr_torrentRecheckCompleteness (tr_torrent * tor)
2039{
2040    tr_completeness completeness;
2041
2042    assert (tr_isTorrent (tor));
2043
2044    tr_torrentLock (tor);
2045
2046    completeness = tr_cpGetStatus (&tor->completion);
2047
2048    if (completeness != tor->completeness)
2049    {
2050        const int recentChange = tor->downloadedCur != 0;
2051        const bool wasLeeching = !tr_torrentIsSeed (tor);
2052        const bool wasRunning = tor->isRunning;
2053
2054        if (recentChange)
2055        {
2056            tr_torinf (tor, _ ("State changed from \"%1$s\" to \"%2$s\""),
2057                      getCompletionString (tor->completeness),
2058                      getCompletionString (completeness));
2059        }
2060
2061        tor->completeness = completeness;
2062        tr_fdTorrentClose (tor->session, tor->uniqueId);
2063
2064        fireCompletenessChange (tor, completeness, wasRunning);
2065
2066        if (tr_torrentIsSeed (tor))
2067        {
2068            if (recentChange)
2069            {
2070                tr_announcerTorrentCompleted (tor);
2071                tor->doneDate = tor->anyDate = tr_time ();
2072            }
2073
2074            if (wasLeeching && wasRunning)
2075            {
2076                /* clear interested flag on all peers */
2077                tr_peerMgrClearInterest (tor);
2078
2079                /* if completeness was TR_LEECH then the seed limit check will have been skipped in bandwidthPulse */
2080                tr_torrentCheckSeedLimit (tor);
2081            }
2082
2083            if (tor->currentDir == tor->incompleteDir)
2084                tr_torrentSetLocation (tor, tor->downloadDir, true, NULL, NULL);
2085
2086            if (tr_sessionIsTorrentDoneScriptEnabled (tor->session))
2087                torrentCallScript (tor, tr_sessionGetTorrentDoneScript (tor->session));
2088        }
2089
2090        tr_torrentSetDirty (tor);
2091    }
2092
2093    tr_torrentUnlock (tor);
2094}
2095
2096/***
2097****
2098***/
2099
2100static void
2101tr_torrentFireMetadataCompleted (tr_torrent * tor)
2102{
2103    assert (tr_isTorrent (tor));
2104
2105    if (tor->metadata_func)
2106        tor->metadata_func (tor, tor->metadata_func_user_data);
2107}
2108
2109void
2110tr_torrentSetMetadataCallback (tr_torrent                * tor,
2111                               tr_torrent_metadata_func    func,
2112                               void                      * user_data)
2113{
2114    assert (tr_isTorrent (tor));
2115
2116    tor->metadata_func = func;
2117    tor->metadata_func_user_data = user_data;
2118}
2119
2120
2121/**
2122***  File priorities
2123**/
2124
2125void
2126tr_torrentInitFilePriority (tr_torrent *    tor,
2127                            tr_file_index_t fileIndex,
2128                            tr_priority_t   priority)
2129{
2130    tr_piece_index_t i;
2131    tr_file *        file;
2132
2133    assert (tr_isTorrent (tor));
2134    assert (fileIndex < tor->info.fileCount);
2135    assert (tr_isPriority (priority));
2136
2137    file = &tor->info.files[fileIndex];
2138    file->priority = priority;
2139    for (i = file->firstPiece; i <= file->lastPiece; ++i)
2140        tor->info.pieces[i].priority = calculatePiecePriority (tor, i, fileIndex);
2141}
2142
2143void
2144tr_torrentSetFilePriorities (tr_torrent             * tor,
2145                             const tr_file_index_t  * files,
2146                             tr_file_index_t          fileCount,
2147                             tr_priority_t            priority)
2148{
2149    tr_file_index_t i;
2150    assert (tr_isTorrent (tor));
2151    tr_torrentLock (tor);
2152
2153    for (i = 0; i < fileCount; ++i)
2154        if (files[i] < tor->info.fileCount)
2155            tr_torrentInitFilePriority (tor, files[i], priority);
2156    tr_torrentSetDirty (tor);
2157    tr_peerMgrRebuildRequests (tor);
2158
2159    tr_torrentUnlock (tor);
2160}
2161
2162tr_priority_t*
2163tr_torrentGetFilePriorities (const tr_torrent * tor)
2164{
2165    tr_file_index_t i;
2166    tr_priority_t * p;
2167
2168    assert (tr_isTorrent (tor));
2169
2170    tr_torrentLock (tor);
2171    p = tr_new0 (tr_priority_t, tor->info.fileCount);
2172    for (i = 0; i < tor->info.fileCount; ++i)
2173        p[i] = tor->info.files[i].priority;
2174    tr_torrentUnlock (tor);
2175
2176    return p;
2177}
2178
2179/**
2180***  File DND
2181**/
2182
2183static void
2184setFileDND (tr_torrent * tor, tr_file_index_t fileIndex, int doDownload)
2185{
2186    const int8_t     dnd = !doDownload;
2187    tr_piece_index_t firstPiece;
2188    int8_t           firstPieceDND;
2189    tr_piece_index_t lastPiece;
2190    int8_t           lastPieceDND;
2191    tr_file_index_t  i;
2192    tr_file *        file = &tor->info.files[fileIndex];
2193
2194    file->dnd = dnd;
2195    firstPiece = file->firstPiece;
2196    lastPiece = file->lastPiece;
2197
2198    /* can't set the first piece to DND unless
2199       every file using that piece is DND */
2200    firstPieceDND = dnd;
2201    if (fileIndex > 0)
2202    {
2203        for (i = fileIndex - 1; firstPieceDND; --i)
2204        {
2205            if (tor->info.files[i].lastPiece != firstPiece)
2206                break;
2207            firstPieceDND = tor->info.files[i].dnd;
2208            if (!i)
2209                break;
2210        }
2211    }
2212
2213    /* can't set the last piece to DND unless
2214       every file using that piece is DND */
2215    lastPieceDND = dnd;
2216    for (i = fileIndex + 1; lastPieceDND && i < tor->info.fileCount; ++i)
2217    {
2218        if (tor->info.files[i].firstPiece != lastPiece)
2219            break;
2220        lastPieceDND = tor->info.files[i].dnd;
2221    }
2222
2223    if (firstPiece == lastPiece)
2224    {
2225        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
2226    }
2227    else
2228    {
2229        tr_piece_index_t pp;
2230        tor->info.pieces[firstPiece].dnd = firstPieceDND;
2231        tor->info.pieces[lastPiece].dnd = lastPieceDND;
2232        for (pp = firstPiece + 1; pp < lastPiece; ++pp)
2233            tor->info.pieces[pp].dnd = dnd;
2234    }
2235}
2236
2237void
2238tr_torrentInitFileDLs (tr_torrent             * tor,
2239                       const tr_file_index_t  * files,
2240                       tr_file_index_t          fileCount,
2241                       bool                     doDownload)
2242{
2243    tr_file_index_t i;
2244
2245    assert (tr_isTorrent (tor));
2246
2247    tr_torrentLock (tor);
2248
2249    for (i=0; i<fileCount; ++i)
2250        if (files[i] < tor->info.fileCount)
2251            setFileDND (tor, files[i], doDownload);
2252
2253    tr_cpInvalidateDND (&tor->completion);
2254
2255    tr_torrentUnlock (tor);
2256}
2257
2258void
2259tr_torrentSetFileDLs (tr_torrent             * tor,
2260                      const tr_file_index_t  * files,
2261                      tr_file_index_t          fileCount,
2262                      bool                     doDownload)
2263{
2264    assert (tr_isTorrent (tor));
2265    tr_torrentLock (tor);
2266
2267    tr_torrentInitFileDLs (tor, files, fileCount, doDownload);
2268    tr_torrentSetDirty (tor);
2269    tr_peerMgrRebuildRequests (tor);
2270
2271    tr_torrentUnlock (tor);
2272}
2273
2274/***
2275****
2276***/
2277
2278tr_priority_t
2279tr_torrentGetPriority (const tr_torrent * tor)
2280{
2281    assert (tr_isTorrent (tor));
2282
2283    return tor->bandwidth.priority;
2284}
2285
2286void
2287tr_torrentSetPriority (tr_torrent * tor, tr_priority_t priority)
2288{
2289    assert (tr_isTorrent (tor));
2290    assert (tr_isPriority (priority));
2291
2292    if (tor->bandwidth.priority != priority)
2293    {
2294        tor->bandwidth.priority = priority;
2295
2296        tr_torrentSetDirty (tor);
2297    }
2298}
2299
2300/***
2301****
2302***/
2303
2304void
2305tr_torrentSetPeerLimit (tr_torrent * tor,
2306                        uint16_t     maxConnectedPeers)
2307{
2308    assert (tr_isTorrent (tor));
2309
2310    if (tor->maxConnectedPeers != maxConnectedPeers)
2311    {
2312        tor->maxConnectedPeers = maxConnectedPeers;
2313
2314        tr_torrentSetDirty (tor);
2315    }
2316}
2317
2318uint16_t
2319tr_torrentGetPeerLimit (const tr_torrent * tor)
2320{
2321    assert (tr_isTorrent (tor));
2322
2323    return tor->maxConnectedPeers;
2324}
2325
2326/***
2327****
2328***/
2329
2330void
2331tr_torrentGetBlockLocation (const tr_torrent * tor,
2332                            tr_block_index_t   block,
2333                            tr_piece_index_t * piece,
2334                            uint32_t         * offset,
2335                            uint32_t         * length)
2336{
2337    uint64_t pos = block;
2338    pos *= tor->blockSize;
2339    *piece = pos / tor->info.pieceSize;
2340    *offset = pos - (*piece * tor->info.pieceSize);
2341    *length = tr_torBlockCountBytes (tor, block);
2342}
2343
2344
2345tr_block_index_t
2346_tr_block (const tr_torrent * tor,
2347           tr_piece_index_t   index,
2348           uint32_t           offset)
2349{
2350    tr_block_index_t ret;
2351
2352    assert (tr_isTorrent (tor));
2353
2354    ret = index;
2355    ret *= (tor->info.pieceSize / tor->blockSize);
2356    ret += offset / tor->blockSize;
2357    return ret;
2358}
2359
2360bool
2361tr_torrentReqIsValid (const tr_torrent * tor,
2362                      tr_piece_index_t   index,
2363                      uint32_t           offset,
2364                      uint32_t           length)
2365{
2366    int err = 0;
2367
2368    assert (tr_isTorrent (tor));
2369
2370    if (index >= tor->info.pieceCount)
2371        err = 1;
2372    else if (length < 1)
2373        err = 2;
2374    else if ((offset + length) > tr_torPieceCountBytes (tor, index))
2375        err = 3;
2376    else if (length > MAX_BLOCK_SIZE)
2377        err = 4;
2378    else if (tr_pieceOffset (tor, index, offset, length) > tor->info.totalSize)
2379        err = 5;
2380
2381    if (err) tr_tordbg (tor, "index %lu offset %lu length %lu err %d\n",
2382                            (unsigned long)index,
2383                            (unsigned long)offset,
2384                            (unsigned long)length,
2385                              err);
2386
2387    return !err;
2388}
2389
2390uint64_t
2391tr_pieceOffset (const tr_torrent * tor,
2392                tr_piece_index_t   index,
2393                uint32_t           offset,
2394                uint32_t           length)
2395{
2396    uint64_t ret;
2397
2398    assert (tr_isTorrent (tor));
2399
2400    ret = tor->info.pieceSize;
2401    ret *= index;
2402    ret += offset;
2403    ret += length;
2404    return ret;
2405}
2406
2407void
2408tr_torGetFileBlockRange (const tr_torrent        * tor,
2409                         const tr_file_index_t     file,
2410                         tr_block_index_t        * first,
2411                         tr_block_index_t        * last)
2412{
2413    const tr_file * f = &tor->info.files[file];
2414    uint64_t offset = f->offset;
2415    *first = offset / tor->blockSize;
2416    if (!f->length)
2417        *last = *first;
2418    else {
2419        offset += f->length - 1;
2420        *last = offset / tor->blockSize;
2421    }
2422}
2423
2424void
2425tr_torGetPieceBlockRange (const tr_torrent        * tor,
2426                          const tr_piece_index_t    piece,
2427                          tr_block_index_t        * first,
2428                          tr_block_index_t        * last)
2429{
2430    uint64_t offset = tor->info.pieceSize;
2431    offset *= piece;
2432    *first = offset / tor->blockSize;
2433    offset += (tr_torPieceCountBytes (tor, piece) - 1);
2434    *last = offset / tor->blockSize;
2435}
2436
2437
2438/***
2439****
2440***/
2441
2442void
2443tr_torrentSetPieceChecked (tr_torrent * tor, tr_piece_index_t pieceIndex)
2444{
2445    assert (tr_isTorrent (tor));
2446    assert (pieceIndex < tor->info.pieceCount);
2447
2448    tor->info.pieces[pieceIndex].timeChecked = tr_time ();
2449}
2450
2451void
2452tr_torrentSetChecked (tr_torrent * tor, time_t when)
2453{
2454    tr_piece_index_t i, n;
2455
2456    assert (tr_isTorrent (tor));
2457
2458    for (i=0, n=tor->info.pieceCount; i!=n; ++i)
2459        tor->info.pieces[i].timeChecked = when;
2460}
2461
2462bool
2463tr_torrentCheckPiece (tr_torrent * tor, tr_piece_index_t pieceIndex)
2464{
2465    const bool pass = tr_ioTestPiece (tor, pieceIndex);
2466
2467    tr_deeplog_tor (tor, "[LAZY] tr_torrentCheckPiece tested piece %zu, pass==%d", (size_t)pieceIndex, (int)pass);
2468    tr_torrentSetHasPiece (tor, pieceIndex, pass);
2469    tr_torrentSetPieceChecked (tor, pieceIndex);
2470    tor->anyDate = tr_time ();
2471    tr_torrentSetDirty (tor);
2472
2473    return pass;
2474}
2475
2476time_t
2477tr_torrentGetFileMTime (const tr_torrent * tor, tr_file_index_t i)
2478{
2479    time_t mtime = 0;
2480    if (!tr_fdFileGetCachedMTime (tor->session, tor->uniqueId, i, &mtime))
2481        tr_torrentFindFile2 (tor, i, NULL, NULL, &mtime);
2482    return mtime;
2483}
2484
2485bool
2486tr_torrentPieceNeedsCheck (const tr_torrent * tor, tr_piece_index_t p)
2487{
2488    uint64_t unused;
2489    tr_file_index_t f;
2490    const tr_info * inf = tr_torrentInfo (tor);
2491
2492    /* if we've never checked this piece, then it needs to be checked */
2493    if (!inf->pieces[p].timeChecked)
2494        return true;
2495
2496    /* If we think we've completed one of the files in this piece,
2497     * but it's been modified since we last checked it,
2498     * then it needs to be rechecked */
2499    tr_ioFindFileLocation (tor, p, 0, &f, &unused);
2500    for (; f < inf->fileCount && pieceHasFile (p, &inf->files[f]); ++f)
2501        if (tr_cpFileIsComplete (&tor->completion, f))
2502            if (tr_torrentGetFileMTime (tor, f) > inf->pieces[p].timeChecked)
2503                return true;
2504
2505    return false;
2506}
2507
2508/***
2509****
2510***/
2511
2512static int
2513compareTrackerByTier (const void * va, const void * vb)
2514{
2515    const tr_tracker_info * a = va;
2516    const tr_tracker_info * b = vb;
2517
2518    /* sort by tier */
2519    if (a->tier != b->tier)
2520        return a->tier - b->tier;
2521
2522    /* get the effects of a stable sort by comparing the two elements' addresses */
2523    return a - b;
2524}
2525
2526bool
2527tr_torrentSetAnnounceList (tr_torrent             * tor,
2528                           const tr_tracker_info  * trackers_in,
2529                           int                      trackerCount)
2530{
2531    int i;
2532    tr_benc metainfo;
2533    bool ok = true;
2534    tr_tracker_info * trackers;
2535
2536    tr_torrentLock (tor);
2537
2538    assert (tr_isTorrent (tor));
2539
2540    /* ensure the trackers' tiers are in ascending order */
2541    trackers = tr_memdup (trackers_in, sizeof (tr_tracker_info) * trackerCount);
2542    qsort (trackers, trackerCount, sizeof (tr_tracker_info), compareTrackerByTier);
2543
2544    /* look for bad URLs */
2545    for (i=0; ok && i<trackerCount; ++i)
2546        if (!tr_urlIsValidTracker (trackers[i].announce))
2547            ok = false;
2548
2549    /* save to the .torrent file */
2550    if (ok && !tr_bencLoadFile (&metainfo, TR_FMT_BENC, tor->info.torrent))
2551    {
2552        bool hasInfo;
2553        tr_info tmpInfo;
2554
2555        /* remove the old fields */
2556        tr_bencDictRemove (&metainfo, "announce");
2557        tr_bencDictRemove (&metainfo, "announce-list");
2558
2559        /* add the new fields */
2560        if (trackerCount > 0)
2561        {
2562            tr_bencDictAddStr (&metainfo, "announce", trackers[0].announce);
2563        }
2564        if (trackerCount > 1)
2565        {
2566            int i;
2567            int prevTier = -1;
2568            tr_benc * tier = NULL;
2569            tr_benc * announceList = tr_bencDictAddList (&metainfo, "announce-list", 0);
2570
2571            for (i=0; i<trackerCount; ++i) {
2572                if (prevTier != trackers[i].tier) {
2573                    prevTier = trackers[i].tier;
2574                    tier = tr_bencListAddList (announceList, 0);
2575                }
2576                tr_bencListAddStr (tier, trackers[i].announce);
2577            }
2578        }
2579
2580        /* try to parse it back again, to make sure it's good */
2581        memset (&tmpInfo, 0, sizeof (tr_info));
2582        if (tr_metainfoParse (tor->session, &metainfo, &tmpInfo,
2583                              &hasInfo, &tor->infoDictLength))
2584        {
2585            /* it's good, so keep these new trackers and free the old ones */
2586
2587            tr_info swap;
2588            swap.trackers = tor->info.trackers;
2589            swap.trackerCount = tor->info.trackerCount;
2590            tor->info.trackers = tmpInfo.trackers;
2591            tor->info.trackerCount = tmpInfo.trackerCount;
2592            tmpInfo.trackers = swap.trackers;
2593            tmpInfo.trackerCount = swap.trackerCount;
2594
2595            tr_metainfoFree (&tmpInfo);
2596            tr_bencToFile (&metainfo, TR_FMT_BENC, tor->info.torrent);
2597        }
2598
2599        /* cleanup */
2600        tr_bencFree (&metainfo);
2601
2602        /* if we had a tracker-related error on this torrent,
2603         * and that tracker's been removed,
2604         * then clear the error */
2605        if ((tor->error == TR_STAT_TRACKER_WARNING)
2606            || (tor->error == TR_STAT_TRACKER_ERROR))
2607        {
2608            bool clear = true;
2609
2610            for (i=0; clear && i<trackerCount; ++i)
2611                if (!strcmp (trackers[i].announce, tor->errorTracker))
2612                    clear = false;
2613
2614            if (clear)
2615                tr_torrentClearError (tor);
2616        }
2617
2618        /* tell the announcer to reload this torrent's tracker list */
2619        tr_announcerResetTorrent (tor->session->announcer, tor);
2620    }
2621
2622    tr_torrentUnlock (tor);
2623
2624    tr_free (trackers);
2625    return ok;
2626}
2627
2628/**
2629***
2630**/
2631
2632void
2633tr_torrentSetAddedDate (tr_torrent * tor,
2634                        time_t       t)
2635{
2636    assert (tr_isTorrent (tor));
2637
2638    tor->addedDate = t;
2639    tor->anyDate = MAX (tor->anyDate, tor->addedDate);
2640}
2641
2642void
2643tr_torrentSetActivityDate (tr_torrent * tor, time_t t)
2644{
2645    assert (tr_isTorrent (tor));
2646
2647    tor->activityDate = t;
2648    tor->anyDate = MAX (tor->anyDate, tor->activityDate);
2649}
2650
2651void
2652tr_torrentSetDoneDate (tr_torrent * tor,
2653                       time_t       t)
2654{
2655    assert (tr_isTorrent (tor));
2656
2657    tor->doneDate = t;
2658    tor->anyDate = MAX (tor->anyDate, tor->doneDate);
2659}
2660
2661/**
2662***
2663**/
2664
2665uint64_t
2666tr_torrentGetBytesLeftToAllocate (const tr_torrent * tor)
2667{
2668    tr_file_index_t i;
2669    uint64_t bytesLeft = 0;
2670
2671    assert (tr_isTorrent (tor));
2672
2673    for (i=0; i<tor->info.fileCount; ++i)
2674    {
2675        if (!tor->info.files[i].dnd)
2676        {
2677            struct stat sb;
2678            const uint64_t length = tor->info.files[i].length;
2679            char * path = tr_torrentFindFile (tor, i);
2680
2681            bytesLeft += length;
2682
2683            if ((path != NULL) && !stat (path, &sb)
2684                                 && S_ISREG (sb.st_mode)
2685                                 && ((uint64_t)sb.st_size <= length))
2686                bytesLeft -= sb.st_size;
2687
2688            tr_free (path);
2689        }
2690    }
2691
2692    return bytesLeft;
2693}
2694
2695/****
2696*****  Removing the torrent's local data
2697****/
2698
2699static bool
2700isJunkFile (const char * base)
2701{
2702    int i;
2703    static const char * files[] = { ".DS_Store", "desktop.ini", "Thumbs.db" };
2704    static const int file_count = sizeof (files) / sizeof (files[0]);
2705
2706    for (i=0; i<file_count; ++i)
2707        if (!strcmp (base, files[i]))
2708            return true;
2709
2710#ifdef SYS_DARWIN
2711    /* check for resource forks. <http://support.apple.com/kb/TA20578> */
2712    if (!memcmp (base, "._", 2))
2713        return true;
2714#endif
2715
2716    return false;
2717}
2718
2719static void
2720removeEmptyFoldersAndJunkFiles (const char * folder)
2721{
2722    DIR * odir;
2723    if ((odir = opendir (folder))) {
2724        struct dirent * d;
2725        while ((d = readdir (odir))) {
2726            if (strcmp (d->d_name, ".") && strcmp (d->d_name, "..")) {
2727                struct stat sb;
2728                char * filename = tr_buildPath (folder, d->d_name, NULL);
2729                if (!stat (filename, &sb) && S_ISDIR (sb.st_mode))
2730                    removeEmptyFoldersAndJunkFiles (filename);
2731                else if (isJunkFile (d->d_name))
2732                    remove (filename);
2733                tr_free (filename);
2734            }
2735        }
2736        remove (folder);
2737        closedir (odir);
2738    }
2739}
2740
2741/**
2742 * This convoluted code does something (seemingly) simple:
2743 * remove the torrent's local files.
2744 *
2745 * Fun complications:
2746 * 1. Try to preserve the directory hierarchy in the recycle bin.
2747 * 2. If there are nontorrent files, don't delete them...
2748 * 3. ...unless the other files are "junk", such as .DS_Store
2749 */
2750static void
2751deleteLocalData (tr_torrent * tor, tr_fileFunc func)
2752{
2753    int i, n;
2754    tr_file_index_t f;
2755    char * base;
2756    DIR * odir;
2757    char * tmpdir = NULL;
2758    tr_ptrArray files = TR_PTR_ARRAY_INIT;
2759    tr_ptrArray folders = TR_PTR_ARRAY_INIT;
2760    const void * const vstrcmp = strcmp;
2761    const char * const top = tor->currentDir;
2762
2763    /* if it's a magnet link, there's nothing to move... */
2764    if (!tr_torrentHasMetadata (tor))
2765        return;
2766
2767    /***
2768    ****  Move the local data to a new tmpdir
2769    ***/
2770
2771    base = tr_strdup_printf ("%s__XXXXXX", tr_torrentName (tor));
2772    tmpdir = tr_buildPath (top, base, NULL);
2773    tr_mkdtemp (tmpdir);
2774    tr_free (base);
2775
2776    for (f=0; f<tor->info.fileCount; ++f)
2777    {
2778        char * filename = tr_buildPath (top, tor->info.files[f].name, NULL);
2779        if (!tr_fileExists (filename, NULL)) {
2780                char * partial = tr_torrentBuildPartial (tor, f);
2781                tr_free (filename);
2782                filename = tr_buildPath (top, partial, NULL);
2783                tr_free (partial);
2784                if (!tr_fileExists (filename, NULL)) {
2785                        tr_free (filename);
2786                        filename = NULL;
2787                }
2788        }
2789
2790        if (filename != NULL)
2791        {
2792            char * target = tr_buildPath (tmpdir, tor->info.files[f].name, NULL);
2793            char * target_dir = tr_dirname (target);
2794            tr_mkdirp (target_dir, 0777);
2795            rename (filename, target);
2796            tr_ptrArrayAppend (&files, target);
2797            tr_free (target_dir);
2798            tr_free (filename);
2799        }
2800    }
2801
2802    /***
2803    ****  Remove tmpdir.
2804    ****
2805    ****  Try deleting the top-level files & folders to preserve
2806    ****  the directory hierarchy in the recycle bin.
2807    ****  If case that fails -- for example, rmdir () doesn't
2808    ****  delete nonempty folders -- go from the bottom up too.
2809    ***/
2810
2811    /* try deleting the local data's top-level files & folders */
2812    if ((odir = opendir (tmpdir)))
2813    {
2814        struct dirent * d;
2815        while ((d = readdir (odir)))
2816        {
2817            if (strcmp (d->d_name, ".") && strcmp (d->d_name, ".."))
2818            {
2819                char * file = tr_buildPath (tmpdir, d->d_name, NULL);
2820                func (file);
2821                tr_free (file);
2822            }
2823        }
2824        closedir (odir);
2825    }
2826
2827    /* go from the bottom up */
2828    for (i=0, n=tr_ptrArraySize (&files); i<n; ++i)
2829    {
2830        char * walk = tr_strdup (tr_ptrArrayNth (&files, i));
2831        while (tr_fileExists (walk, NULL) && !tr_is_same_file (tmpdir, walk))
2832        {
2833            char * tmp = tr_dirname (walk);
2834            func (walk);
2835            tr_free (walk);
2836            walk = tmp;
2837        }
2838        tr_free (walk);
2839    }
2840
2841    /***
2842    ****  The local data has been removed.
2843    ****  What's left in top are empty folders, junk, and user-generated files.
2844    ****  Remove the first two categories and leave the third.
2845    ***/
2846
2847    /* build a list of 'top's child directories that belong to this torrent */
2848    for (f=0; f<tor->info.fileCount; ++f)
2849    {
2850        /* get the directory that this file goes in... */
2851        char * filename = tr_buildPath (top, tor->info.files[f].name, NULL);
2852        char * dir = tr_dirname (filename);
2853        if (!tr_is_same_file (top, dir) && strcmp (top, dir)) {
2854            for (;;) {
2855                char * parent = tr_dirname (dir);
2856                if (tr_is_same_file (top, parent) || !strcmp (top, parent)) {
2857                    if (tr_ptrArrayFindSorted (&folders, dir, vstrcmp) == NULL) {
2858                        tr_ptrArrayInsertSorted (&folders, tr_strdup (dir), vstrcmp);
2859                    }
2860                    break;
2861                }
2862                tr_free (dir);
2863                dir = parent;
2864            }
2865        }
2866        tr_free (dir);
2867        tr_free (filename);
2868    }
2869    for (i=0, n=tr_ptrArraySize (&folders); i<n; ++i)
2870        removeEmptyFoldersAndJunkFiles (tr_ptrArrayNth (&folders, i));
2871
2872    /* cleanup */
2873    rmdir (tmpdir);
2874    tr_free (tmpdir);
2875    tr_ptrArrayDestruct (&folders, tr_free);
2876    tr_ptrArrayDestruct (&files, tr_free);
2877}
2878
2879static void
2880tr_torrentDeleteLocalData (tr_torrent * tor, tr_fileFunc func)
2881{
2882    assert (tr_isTorrent (tor));
2883
2884    if (func == NULL)
2885        func = remove;
2886
2887    /* close all the files because we're about to delete them */
2888    tr_cacheFlushTorrent (tor->session->cache, tor);
2889    tr_fdTorrentClose (tor->session, tor->uniqueId);
2890
2891    deleteLocalData (tor, func);
2892}
2893
2894/***
2895****
2896***/
2897
2898struct LocationData
2899{
2900    bool move_from_old_location;
2901    volatile int * setme_state;
2902    volatile double * setme_progress;
2903    char * location;
2904    tr_torrent * tor;
2905};
2906
2907static void
2908setLocation (void * vdata)
2909{
2910    bool err = false;
2911    struct LocationData * data = vdata;
2912    tr_torrent * tor = data->tor;
2913    const bool do_move = data->move_from_old_location;
2914    const char * location = data->location;
2915    double bytesHandled = 0;
2916
2917    assert (tr_isTorrent (tor));
2918
2919    tr_dbg ("Moving \"%s\" location from currentDir \"%s\" to \"%s\"",
2920            tr_torrentName (tor), tor->currentDir, location);
2921
2922    tr_mkdirp (location, 0777);
2923
2924    if (!tr_is_same_file (location, tor->currentDir))
2925    {
2926        tr_file_index_t i;
2927
2928        /* bad idea to move files while they're being verified... */
2929        tr_verifyRemove (tor);
2930
2931        /* try to move the files.
2932         * FIXME: there are still all kinds of nasty cases, like what
2933         * if the target directory runs out of space halfway through... */
2934        for (i=0; !err && i<tor->info.fileCount; ++i)
2935        {
2936            const tr_file * f = &tor->info.files[i];
2937            const char * oldbase;
2938            char * sub;
2939            if (tr_torrentFindFile2 (tor, i, &oldbase, &sub, NULL))
2940            {
2941                char * oldpath = tr_buildPath (oldbase, sub, NULL);
2942                char * newpath = tr_buildPath (location, sub, NULL);
2943
2944                tr_dbg ("Found file #%d: %s", (int)i, oldpath);
2945
2946                if (do_move && !tr_is_same_file (oldpath, newpath))
2947                {
2948                    bool renamed = false;
2949                    errno = 0;
2950                    tr_torinf (tor, "moving \"%s\" to \"%s\"", oldpath, newpath);
2951                    if (tr_moveFile (oldpath, newpath, &renamed))
2952                    {
2953                        err = true;
2954                        tr_torerr (tor, "error moving \"%s\" to \"%s\": %s",
2955                                        oldpath, newpath, tr_strerror (errno));
2956                    }
2957                }
2958
2959                tr_free (newpath);
2960                tr_free (oldpath);
2961                tr_free (sub);
2962            }
2963
2964            if (data->setme_progress)
2965            {
2966                bytesHandled += f->length;
2967                *data->setme_progress = bytesHandled / tor->info.totalSize;
2968            }
2969        }
2970
2971        if (!err)
2972        {
2973            /* blow away the leftover subdirectories in the old location */
2974            if (do_move)
2975                tr_torrentDeleteLocalData (tor, remove);
2976
2977            /* set the new location and reverify */
2978            tr_torrentSetDownloadDir (tor, location);
2979        }
2980    }
2981
2982    if (!err && do_move)
2983    {
2984        tr_free (tor->incompleteDir);
2985        tor->incompleteDir = NULL;
2986        tor->currentDir = tor->downloadDir;
2987    }
2988
2989    if (data->setme_state)
2990        *data->setme_state = err ? TR_LOC_ERROR : TR_LOC_DONE;
2991
2992    /* cleanup */
2993    tr_free (data->location);
2994    tr_free (data);
2995}
2996
2997void
2998tr_torrentSetLocation (tr_torrent       * tor,
2999                       const char       * location,
3000                       bool               move_from_old_location,
3001                       volatile double  * setme_progress,
3002                       volatile int     * setme_state)
3003{
3004    struct LocationData * data;
3005
3006    assert (tr_isTorrent (tor));
3007
3008    if (setme_state)
3009        *setme_state = TR_LOC_MOVING;
3010    if (setme_progress)
3011        *setme_progress = 0;
3012
3013    /* run this in the libtransmission thread */
3014    data = tr_new (struct LocationData, 1);
3015    data->tor = tor;
3016    data->location = tr_strdup (location);
3017    data->move_from_old_location = move_from_old_location;
3018    data->setme_state = setme_state;
3019    data->setme_progress = setme_progress;
3020    tr_runInEventThread (tor->session, setLocation, data);
3021}
3022
3023/***
3024****
3025***/
3026
3027void
3028tr_torrentFileCompleted (tr_torrent * tor, tr_file_index_t fileNum)
3029{
3030    char * sub;
3031    const char * base;
3032    const tr_info * inf = &tor->info;
3033    const tr_file * f = &inf->files[fileNum];
3034    tr_piece * p;
3035    const tr_piece * pend;
3036    const time_t now = tr_time ();
3037
3038    /* close the file so that we can reopen in read-only mode as needed */
3039    tr_fdFileClose (tor->session, tor, fileNum);
3040
3041    /* now that the file is complete and closed, we can start watching its
3042     * mtime timestamp for changes to know if we need to reverify pieces */
3043    for (p=&inf->pieces[f->firstPiece], pend=&inf->pieces[f->lastPiece]; p!=pend; ++p)
3044        p->timeChecked = now;
3045
3046    /* if the torrent's current filename isn't the same as the one in the
3047     * metadata -- for example, if it had the ".part" suffix appended to
3048     * it until now -- then rename it to match the one in the metadata */
3049    if (tr_torrentFindFile2 (tor, fileNum, &base, &sub, NULL))
3050    {
3051        if (strcmp (sub, f->name))
3052        {
3053            char * oldpath = tr_buildPath (base, sub, NULL);
3054            char * newpath = tr_buildPath (base, f->name, NULL);
3055
3056            if (rename (oldpath, newpath))
3057                tr_torerr (tor, "Error moving \"%s\" to \"%s\": %s", oldpath, newpath, tr_strerror (errno));
3058
3059            tr_free (newpath);
3060            tr_free (oldpath);
3061        }
3062
3063        tr_free (sub);
3064    }
3065}
3066
3067/***
3068****
3069***/
3070
3071bool
3072tr_torrentFindFile2 (const tr_torrent * tor, tr_file_index_t fileNum,
3073                     const char ** base, char ** subpath, time_t * mtime)
3074{
3075    char * part = NULL;
3076    const tr_file * file;
3077    const char * b = NULL;
3078    const char * s = NULL;
3079
3080    assert (tr_isTorrent (tor));
3081    assert (fileNum < tor->info.fileCount);
3082
3083    file = &tor->info.files[fileNum];
3084
3085    if (b == NULL) {
3086        char * filename = tr_buildPath (tor->downloadDir, file->name, NULL);
3087        if (tr_fileExists (filename, mtime)) {
3088            b = tor->downloadDir;
3089            s = file->name;
3090        }
3091        tr_free (filename);
3092    }
3093
3094    if ((b == NULL) && (tor->incompleteDir != NULL)) {
3095        char * filename = tr_buildPath (tor->incompleteDir, file->name, NULL);
3096        if (tr_fileExists (filename, mtime)) {
3097            b = tor->incompleteDir;
3098            s = file->name;
3099        }
3100        tr_free (filename);
3101    }
3102
3103    if (b == NULL)
3104        part = tr_torrentBuildPartial (tor, fileNum);
3105
3106    if ((b == NULL) && (tor->incompleteDir != NULL)) {
3107        char * filename = tr_buildPath (tor->incompleteDir, part, NULL);
3108        if (tr_fileExists (filename, mtime)) {
3109            b = tor->incompleteDir;
3110            s = part;
3111        }
3112        tr_free (filename);
3113    }
3114
3115    if (b == NULL) {
3116        char * filename = tr_buildPath (tor->downloadDir, part, NULL);
3117        if (tr_fileExists (filename, mtime)) {
3118            b = tor->downloadDir;
3119            s = part;
3120        }
3121        tr_free (filename);
3122    }
3123
3124    if (base != NULL)
3125        *base = b;
3126    if (subpath != NULL)
3127        *subpath = tr_strdup (s);
3128
3129    tr_free (part);
3130    return b != NULL;
3131}
3132
3133char*
3134tr_torrentFindFile (const tr_torrent * tor, tr_file_index_t fileNum)
3135{
3136    char * subpath;
3137    char * ret = NULL;
3138    const char * base;
3139
3140    if (tr_torrentFindFile2 (tor, fileNum, &base, &subpath, NULL))
3141    {
3142        ret = tr_buildPath (base, subpath, NULL);
3143        tr_free (subpath);
3144    }
3145
3146    return ret;
3147}
3148
3149/* Decide whether we should be looking for files in downloadDir or incompleteDir. */
3150static void
3151refreshCurrentDir (tr_torrent * tor)
3152{
3153    const char * dir = NULL;
3154
3155    if (tor->incompleteDir == NULL)
3156        dir = tor->downloadDir;
3157    else if (!tr_torrentHasMetadata (tor)) /* no files to find */
3158        dir = tor->incompleteDir;
3159    else if (!tr_torrentFindFile2 (tor, 0, &dir, NULL, NULL))
3160        dir = tor->incompleteDir;
3161
3162    assert (dir != NULL);
3163    assert ((dir == tor->downloadDir) || (dir == tor->incompleteDir));
3164    tor->currentDir = dir;
3165}
3166
3167char*
3168tr_torrentBuildPartial (const tr_torrent * tor, tr_file_index_t fileNum)
3169{
3170    return tr_strdup_printf ("%s.part", tor->info.files[fileNum].name);
3171}
3172
3173/***
3174****
3175***/
3176
3177static int
3178compareTorrentByQueuePosition (const void * va, const void * vb)
3179{
3180    const tr_torrent * a = * (const tr_torrent **) va;
3181    const tr_torrent * b = * (const tr_torrent **) vb;
3182
3183    return a->queuePosition - b->queuePosition;
3184}
3185
3186#ifndef NDEBUG
3187static bool
3188queueIsSequenced (tr_session * session)
3189{
3190    int i ;
3191    int n ;
3192    bool is_sequenced = true;
3193    tr_torrent * tor;
3194    tr_torrent ** tmp = tr_new (tr_torrent *, session->torrentCount);
3195
3196    /* get all the torrents */
3197    n = 0;
3198    tor = NULL;
3199    while ((tor = tr_torrentNext (session, tor)))
3200        tmp[n++] = tor;
3201
3202    /* sort them by position */
3203    qsort (tmp, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3204
3205#if 0
3206    fprintf (stderr, "%s", "queue: ");
3207    for (i=0; i<n; ++i)
3208        fprintf (stderr, "%d ", tmp[i]->queuePosition);
3209    fputc ('\n', stderr);
3210#endif
3211
3212    /* test them */
3213    for (i=0; is_sequenced && i<n; ++i)
3214        if (tmp[i]->queuePosition != i)
3215            is_sequenced = false;
3216
3217    tr_free (tmp);
3218    return is_sequenced;
3219}
3220#endif
3221
3222int
3223tr_torrentGetQueuePosition (const tr_torrent * tor)
3224{
3225    return tor->queuePosition;
3226}
3227
3228void
3229tr_torrentSetQueuePosition (tr_torrent * tor, int pos)
3230{
3231    int back = -1;
3232    tr_torrent * walk;
3233    const int old_pos = tor->queuePosition;
3234    const time_t now = tr_time ();
3235
3236    if (pos < 0)
3237        pos = 0;
3238
3239    tor->queuePosition = -1;
3240
3241    walk = NULL;
3242    while ((walk = tr_torrentNext (tor->session, walk)))
3243    {
3244        if (old_pos < pos) {
3245            if ((old_pos <= walk->queuePosition) && (walk->queuePosition <= pos)) {
3246                walk->queuePosition--;
3247                walk->anyDate = now;
3248            }
3249        }
3250
3251        if (old_pos > pos) {
3252            if ((pos <= walk->queuePosition) && (walk->queuePosition < old_pos)) {
3253                walk->queuePosition++;
3254                walk->anyDate = now;
3255            }
3256        }
3257
3258        if (back < walk->queuePosition)
3259            back = walk->queuePosition;
3260    }
3261
3262    tor->queuePosition = MIN (pos, (back+1));
3263    tor->anyDate = now;
3264
3265    assert (queueIsSequenced (tor->session));
3266}
3267
3268void
3269tr_torrentsQueueMoveTop (tr_torrent ** torrents_in, int n)
3270{
3271    int i;
3272    tr_torrent ** torrents = tr_memdup (torrents_in, sizeof (tr_torrent *) * n);
3273    qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3274    for (i=n-1; i>=0; --i)
3275        tr_torrentSetQueuePosition (torrents[i], 0);
3276    tr_free (torrents);
3277}
3278
3279void
3280tr_torrentsQueueMoveUp (tr_torrent ** torrents_in, int n)
3281{
3282    int i;
3283    tr_torrent ** torrents = tr_memdup (torrents_in, sizeof (tr_torrent *) * n);
3284    qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3285    for (i=0; i<n; ++i)
3286        tr_torrentSetQueuePosition (torrents[i], torrents[i]->queuePosition - 1);
3287    tr_free (torrents);
3288}
3289
3290void
3291tr_torrentsQueueMoveDown (tr_torrent ** torrents_in, int n)
3292{
3293    int i;
3294    tr_torrent ** torrents = tr_memdup (torrents_in, sizeof (tr_torrent *) * n);
3295    qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3296    for (i=n-1; i>=0; --i)
3297        tr_torrentSetQueuePosition (torrents[i], torrents[i]->queuePosition + 1);
3298    tr_free (torrents);
3299}
3300
3301void
3302tr_torrentsQueueMoveBottom (tr_torrent ** torrents_in, int n)
3303{
3304    int i;
3305    tr_torrent ** torrents = tr_memdup (torrents_in, sizeof (tr_torrent *) * n);
3306    qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3307    for (i=0; i<n; ++i)
3308        tr_torrentSetQueuePosition (torrents[i], INT_MAX);
3309    tr_free (torrents);
3310}
3311
3312static void
3313torrentSetQueued (tr_torrent * tor, bool queued)
3314{
3315    assert (tr_isTorrent (tor));
3316    assert (tr_isBool (queued));
3317
3318    if (tr_torrentIsQueued (tor) != queued)
3319    {
3320        tor->isQueued = queued;
3321        tor->anyDate = tr_time ();
3322    }
3323}
3324
3325void
3326tr_torrentSetQueueStartCallback (tr_torrent * torrent, void (*callback)(tr_torrent *, void *), void * user_data)
3327{
3328    torrent->queue_started_callback = callback;
3329    torrent->queue_started_user_data = user_data;
3330}
3331
3332
Note: See TracBrowser for help on using the repository browser.