source: trunk/libtransmission/torrent.c @ 13880

Last change on this file since 13880 was 13880, checked in by jordan, 8 years ago

#5146 'seeding complete callback called twice': revert r13627 due to issues described in https://trac.transmissionbt.com/ticket/5146#comment:15

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