source: trunk/libtransmission/torrent.c @ 13913

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

(libT) change the API signature for tr_torrentVerify() s.t. client code can be notified when the verify is finished

  • Property svn:keywords set to Date Rev Author Id
File size: 92.6 KB
Line 
1/*
2 * This file Copyright (C) Mnemosyne LLC
3 *
4 * This file is licensed by the GPL version 2. Works owned by the
5 * Transmission project are granted a special exemption to clause 2 (b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
9 *
10 * $Id: torrent.c 13913 2013-01-31 21:58:25Z 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, NULL, NULL);
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
1685struct verify_data
1686{
1687  bool aborted;
1688  tr_torrent * tor;
1689  tr_verify_done_func callback_func;
1690  void * callback_data;
1691};
1692
1693static void
1694onVerifyDoneThreadFunc (void * vdata)
1695{
1696  struct verify_data * data = vdata;
1697  tr_torrent * tor = data->tor;
1698
1699  if (!data->aborted)
1700    tr_torrentRecheckCompleteness (tor);
1701
1702  if (data->callback_func != NULL)
1703    (*data->callback_func)(tor, data->aborted, data->callback_data);
1704
1705  if (!data->aborted && tor->startAfterVerify)
1706    {
1707      tor->startAfterVerify = false;
1708      torrentStart (tor, false);
1709    }
1710
1711  tr_free (data);
1712}
1713
1714static void
1715onVerifyDone (tr_torrent * tor, bool aborted, void * vdata)
1716{
1717  struct verify_data * data = vdata;
1718  assert (data->tor == tor);
1719  data->aborted = aborted;
1720  tr_runInEventThread (tor->session, onVerifyDoneThreadFunc, data);
1721}
1722
1723static void
1724verifyTorrent (void * vdata)
1725{
1726  bool startAfter;
1727  struct verify_data * data = vdata;
1728  tr_torrent * tor = data->tor;
1729  tr_sessionLock (tor->session);
1730
1731  /* if the torrent's already being verified, stop it */
1732  tr_verifyRemove (tor);
1733
1734  startAfter = (tor->isRunning || tor->startAfterVerify) && !tor->isStopping;
1735  if (tor->isRunning)
1736    tr_torrentStop (tor);
1737  tor->startAfterVerify = startAfter;
1738
1739  if (setLocalErrorIfFilesDisappeared (tor))
1740    tor->startAfterVerify = false;
1741  else
1742    tr_verifyAdd (tor, onVerifyDone, data);
1743
1744  tr_sessionUnlock (tor->session);
1745}
1746
1747void
1748tr_torrentVerify (tr_torrent           * tor,
1749                  tr_verify_done_func    callback_func,
1750                  void                 * callback_data)
1751{
1752  struct verify_data * data;
1753
1754  data = tr_new (struct verify_data, 1);
1755  data->tor = tor;
1756  data->aborted = false;
1757  data->callback_func = callback_func;
1758  data->callback_data = callback_data;
1759  tr_runInEventThread (tor->session, verifyTorrent, data);
1760}
1761
1762void
1763tr_torrentSave (tr_torrent * tor)
1764{
1765    assert (tr_isTorrent (tor));
1766
1767    if (tor->isDirty)
1768    {
1769        tor->isDirty = false;
1770        tr_torrentSaveResume (tor);
1771    }
1772}
1773
1774static void
1775stopTorrent (void * vtor)
1776{
1777  tr_torrent * tor = vtor;
1778  tr_logAddTorInfo (tor, "%s", "Pausing");
1779
1780  assert (tr_isTorrent (tor));
1781
1782  tr_torrentLock (tor);
1783
1784  tr_verifyRemove (tor);
1785  torrentSetQueued (tor, false);
1786  tr_peerMgrStopTorrent (tor);
1787  tr_announcerTorrentStopped (tor);
1788  tr_cacheFlushTorrent (tor->session->cache, tor);
1789
1790  tr_fdTorrentClose (tor->session, tor->uniqueId);
1791
1792  if (!tor->isDeleting)
1793    tr_torrentSave (tor);
1794
1795  tr_torrentUnlock (tor);
1796}
1797
1798void
1799tr_torrentStop (tr_torrent * tor)
1800{
1801    assert (tr_isTorrent (tor));
1802
1803    if (tr_isTorrent (tor))
1804    {
1805        tr_sessionLock (tor->session);
1806
1807        tor->isRunning = 0;
1808        tor->isStopping = 0;
1809        tr_torrentSetDirty (tor);
1810        tr_runInEventThread (tor->session, stopTorrent, tor);
1811
1812        tr_sessionUnlock (tor->session);
1813    }
1814}
1815
1816static void
1817closeTorrent (void * vtor)
1818{
1819    tr_variant * d;
1820    tr_torrent * tor = vtor;
1821
1822    assert (tr_isTorrent (tor));
1823
1824    d = tr_variantListAddDict (&tor->session->removedTorrents, 2);
1825    tr_variantDictAddInt (d, TR_KEY_id, tor->uniqueId);
1826    tr_variantDictAddInt (d, TR_KEY_date, tr_time ());
1827
1828    tr_logAddTorInfo (tor, "%s", _("Removing torrent"));
1829
1830    stopTorrent (tor);
1831
1832    if (tor->isDeleting)
1833    {
1834        tr_metainfoRemoveSaved (tor->session, &tor->info);
1835        tr_torrentRemoveResume (tor);
1836    }
1837
1838    tor->isRunning = 0;
1839    freeTorrent (tor);
1840}
1841
1842void
1843tr_torrentFree (tr_torrent * tor) 
1844{
1845  if (tr_isTorrent (tor)) 
1846    { 
1847      tr_session * session = tor->session; 
1848      assert (tr_isSession (session)); 
1849      tr_sessionLock (session); 
1850
1851      tr_torrentClearCompletenessCallback (tor); 
1852      tr_runInEventThread (session, closeTorrent, tor); 
1853
1854      tr_sessionUnlock (session); 
1855    } 
1856}
1857
1858struct remove_data
1859{
1860    tr_torrent   * tor;
1861    bool           deleteFlag;
1862    tr_fileFunc  * deleteFunc;
1863};
1864
1865static void tr_torrentDeleteLocalData (tr_torrent *, tr_fileFunc);
1866
1867static void
1868removeTorrent (void * vdata)
1869{
1870  struct remove_data * data = vdata;
1871  tr_session * session = data->tor->session;
1872  tr_sessionLock (session); 
1873
1874  if (data->deleteFlag)
1875    tr_torrentDeleteLocalData (data->tor, data->deleteFunc);
1876
1877  tr_torrentClearCompletenessCallback (data->tor);
1878  closeTorrent (data->tor);
1879  tr_free (data);
1880
1881  tr_sessionUnlock (session); 
1882}
1883
1884void
1885tr_torrentRemove (tr_torrent   * tor,
1886                  bool           deleteFlag,
1887                  tr_fileFunc    deleteFunc)
1888{
1889    struct remove_data * data;
1890
1891    assert (tr_isTorrent (tor));
1892    tor->isDeleting = 1;
1893
1894    data = tr_new0 (struct remove_data, 1);
1895    data->tor = tor;
1896    data->deleteFlag = deleteFlag;
1897    data->deleteFunc = deleteFunc;
1898    tr_runInEventThread (tor->session, removeTorrent, data);
1899}
1900
1901/**
1902***  Completeness
1903**/
1904
1905static const char *
1906getCompletionString (int type)
1907{
1908    switch (type)
1909    {
1910        /* Translators: this is a minor point that's safe to skip over, but FYI:
1911           "Complete" and "Done" are specific, different terms in Transmission:
1912           "Complete" means we've downloaded every file in the torrent.
1913           "Done" means we're done downloading the files we wanted, but NOT all
1914           that exist */
1915        case TR_PARTIAL_SEED:
1916            return _("Done");
1917
1918        case TR_SEED:
1919            return _("Complete");
1920
1921        default:
1922            return _("Incomplete");
1923    }
1924}
1925
1926static void
1927fireCompletenessChange (tr_torrent       * tor,
1928                        tr_completeness    status,
1929                        bool               wasRunning)
1930{
1931    assert ((status == TR_LEECH)
1932         || (status == TR_SEED)
1933         || (status == TR_PARTIAL_SEED));
1934
1935    if (tor->completeness_func)
1936        tor->completeness_func (tor, status, wasRunning,
1937                                tor->completeness_func_user_data);
1938}
1939
1940void
1941tr_torrentSetCompletenessCallback (tr_torrent                    * tor,
1942                                   tr_torrent_completeness_func    func,
1943                                   void                          * user_data)
1944{
1945    assert (tr_isTorrent (tor));
1946
1947    tor->completeness_func = func;
1948    tor->completeness_func_user_data = user_data;
1949}
1950
1951void
1952tr_torrentClearCompletenessCallback (tr_torrent * torrent)
1953{
1954    tr_torrentSetCompletenessCallback (torrent, NULL, NULL);
1955}
1956
1957void
1958tr_torrentSetRatioLimitHitCallback (tr_torrent                     * tor,
1959                                    tr_torrent_ratio_limit_hit_func  func,
1960                                    void                           * user_data)
1961{
1962    assert (tr_isTorrent (tor));
1963
1964    tor->ratio_limit_hit_func = func;
1965    tor->ratio_limit_hit_func_user_data = user_data;
1966}
1967
1968void
1969tr_torrentClearRatioLimitHitCallback (tr_torrent * torrent)
1970{
1971    tr_torrentSetRatioLimitHitCallback (torrent, NULL, NULL);
1972}
1973
1974void
1975tr_torrentSetIdleLimitHitCallback (tr_torrent                    * tor,
1976                                   tr_torrent_idle_limit_hit_func  func,
1977                                   void                          * user_data)
1978{
1979    assert (tr_isTorrent (tor));
1980
1981    tor->idle_limit_hit_func = func;
1982    tor->idle_limit_hit_func_user_data = user_data;
1983}
1984
1985void
1986tr_torrentClearIdleLimitHitCallback (tr_torrent * torrent)
1987{
1988    tr_torrentSetIdleLimitHitCallback (torrent, NULL, NULL);
1989}
1990
1991static void
1992onSigCHLD (int i UNUSED)
1993{
1994    waitpid (-1, NULL, WNOHANG);
1995}
1996
1997static void
1998torrentCallScript (const tr_torrent * tor, const char * script)
1999{
2000    char timeStr[128];
2001    const time_t now = tr_time ();
2002
2003    tr_strlcpy (timeStr, ctime (&now), sizeof (timeStr));
2004    *strchr (timeStr,'\n') = '\0';
2005
2006    if (script && *script)
2007    {
2008        int i;
2009        char * cmd[] = { tr_strdup (script), NULL };
2010        char * env[] = {
2011            tr_strdup_printf ("TR_APP_VERSION=%s", SHORT_VERSION_STRING),
2012            tr_strdup_printf ("TR_TIME_LOCALTIME=%s", timeStr),
2013            tr_strdup_printf ("TR_TORRENT_DIR=%s", tor->currentDir),
2014            tr_strdup_printf ("TR_TORRENT_ID=%d", tr_torrentId (tor)),
2015            tr_strdup_printf ("TR_TORRENT_HASH=%s", tor->info.hashString),
2016            tr_strdup_printf ("TR_TORRENT_NAME=%s", tr_torrentName (tor)),
2017            NULL };
2018
2019        tr_logAddTorInfo (tor, "Calling script \"%s\"", script);
2020
2021#ifdef WIN32
2022        if (_spawnvpe (_P_NOWAIT, script, (const char*)cmd, env) == -1)
2023          tr_logAddTorErr (tor, "error executing script \"%s\": %s", cmd[0], tr_strerror (errno));
2024#else
2025        signal (SIGCHLD, onSigCHLD);
2026
2027        if (!fork ())
2028        {
2029            for (i=0; env[i]; ++i)
2030                putenv (env[i]);
2031
2032            if (execvp (script, cmd) == -1)
2033              tr_logAddTorErr (tor, "error executing script \"%s\": %s", cmd[0], tr_strerror (errno));
2034
2035            _exit (0);
2036        }
2037#endif
2038
2039        for (i=0; cmd[i]; ++i) tr_free (cmd[i]);
2040        for (i=0; env[i]; ++i) tr_free (env[i]);
2041    }
2042}
2043
2044void
2045tr_torrentRecheckCompleteness (tr_torrent * tor)
2046{
2047  tr_completeness completeness;
2048
2049  tr_torrentLock (tor);
2050
2051  completeness = tr_cpGetStatus (&tor->completion);
2052  if (completeness != tor->completeness)
2053    {
2054      const bool recentChange = tor->downloadedCur != 0;
2055      const bool wasLeeching = !tr_torrentIsSeed (tor);
2056      const bool wasRunning = tor->isRunning;
2057
2058      if (recentChange)
2059        tr_logAddTorInfo (tor, _("State changed from \"%1$s\" to \"%2$s\""),
2060                          getCompletionString (tor->completeness),
2061                          getCompletionString (completeness));
2062
2063      tor->completeness = completeness;
2064      tr_fdTorrentClose (tor->session, tor->uniqueId);
2065
2066      if (tr_torrentIsSeed (tor))
2067        {
2068          if (recentChange)
2069            {
2070              tr_announcerTorrentCompleted (tor);
2071              tor->doneDate = tor->anyDate = tr_time ();
2072            }
2073
2074          if (wasLeeching && wasRunning)
2075            {
2076              /* clear interested flag on all peers */
2077              tr_peerMgrClearInterest (tor);
2078
2079              /* if completeness was TR_LEECH then the seed limit check will have been skipped in bandwidthPulse */
2080              tr_torrentCheckSeedLimit (tor);
2081            }
2082
2083          if (tor->currentDir == tor->incompleteDir)
2084            tr_torrentSetLocation (tor, tor->downloadDir, true, NULL, NULL);
2085
2086          if (tr_sessionIsTorrentDoneScriptEnabled (tor->session))
2087            torrentCallScript (tor, tr_sessionGetTorrentDoneScript (tor->session));
2088        }
2089
2090
2091      fireCompletenessChange (tor, completeness, wasRunning);
2092
2093      tr_torrentSetDirty (tor);
2094    }
2095
2096  tr_torrentUnlock (tor);
2097}
2098
2099/***
2100****
2101***/
2102
2103static void
2104tr_torrentFireMetadataCompleted (tr_torrent * tor)
2105{
2106    assert (tr_isTorrent (tor));
2107
2108    if (tor->metadata_func)
2109        tor->metadata_func (tor, tor->metadata_func_user_data);
2110}
2111
2112void
2113tr_torrentSetMetadataCallback (tr_torrent                * tor,
2114                               tr_torrent_metadata_func    func,
2115                               void                      * user_data)
2116{
2117    assert (tr_isTorrent (tor));
2118
2119    tor->metadata_func = func;
2120    tor->metadata_func_user_data = user_data;
2121}
2122
2123
2124/**
2125***  File priorities
2126**/
2127
2128void
2129tr_torrentInitFilePriority (tr_torrent *    tor,
2130                            tr_file_index_t fileIndex,
2131                            tr_priority_t   priority)
2132{
2133    tr_piece_index_t i;
2134    tr_file *        file;
2135
2136    assert (tr_isTorrent (tor));
2137    assert (fileIndex < tor->info.fileCount);
2138    assert (tr_isPriority (priority));
2139
2140    file = &tor->info.files[fileIndex];
2141    file->priority = priority;
2142    for (i = file->firstPiece; i <= file->lastPiece; ++i)
2143        tor->info.pieces[i].priority = calculatePiecePriority (tor, i, fileIndex);
2144}
2145
2146void
2147tr_torrentSetFilePriorities (tr_torrent             * tor,
2148                             const tr_file_index_t  * files,
2149                             tr_file_index_t          fileCount,
2150                             tr_priority_t            priority)
2151{
2152    tr_file_index_t i;
2153    assert (tr_isTorrent (tor));
2154    tr_torrentLock (tor);
2155
2156    for (i = 0; i < fileCount; ++i)
2157        if (files[i] < tor->info.fileCount)
2158            tr_torrentInitFilePriority (tor, files[i], priority);
2159    tr_torrentSetDirty (tor);
2160    tr_peerMgrRebuildRequests (tor);
2161
2162    tr_torrentUnlock (tor);
2163}
2164
2165tr_priority_t*
2166tr_torrentGetFilePriorities (const tr_torrent * tor)
2167{
2168  tr_file_index_t i;
2169  tr_priority_t * p;
2170
2171  assert (tr_isTorrent (tor));
2172
2173  p = tr_new0 (tr_priority_t, tor->info.fileCount);
2174
2175  for (i=0; i<tor->info.fileCount; ++i)
2176    p[i] = tor->info.files[i].priority;
2177
2178  return p;
2179}
2180
2181/**
2182***  File DND
2183**/
2184
2185static void
2186setFileDND (tr_torrent * tor, tr_file_index_t fileIndex, int doDownload)
2187{
2188    const int8_t     dnd = !doDownload;
2189    tr_piece_index_t firstPiece;
2190    int8_t           firstPieceDND;
2191    tr_piece_index_t lastPiece;
2192    int8_t           lastPieceDND;
2193    tr_file_index_t  i;
2194    tr_file *        file = &tor->info.files[fileIndex];
2195
2196    file->dnd = dnd;
2197    firstPiece = file->firstPiece;
2198    lastPiece = file->lastPiece;
2199
2200    /* can't set the first piece to DND unless
2201       every file using that piece is DND */
2202    firstPieceDND = dnd;
2203    if (fileIndex > 0)
2204    {
2205        for (i = fileIndex - 1; firstPieceDND; --i)
2206        {
2207            if (tor->info.files[i].lastPiece != firstPiece)
2208                break;
2209            firstPieceDND = tor->info.files[i].dnd;
2210            if (!i)
2211                break;
2212        }
2213    }
2214
2215    /* can't set the last piece to DND unless
2216       every file using that piece is DND */
2217    lastPieceDND = dnd;
2218    for (i = fileIndex + 1; lastPieceDND && i < tor->info.fileCount; ++i)
2219    {
2220        if (tor->info.files[i].firstPiece != lastPiece)
2221            break;
2222        lastPieceDND = tor->info.files[i].dnd;
2223    }
2224
2225    if (firstPiece == lastPiece)
2226    {
2227        tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
2228    }
2229    else
2230    {
2231        tr_piece_index_t pp;
2232        tor->info.pieces[firstPiece].dnd = firstPieceDND;
2233        tor->info.pieces[lastPiece].dnd = lastPieceDND;
2234        for (pp = firstPiece + 1; pp < lastPiece; ++pp)
2235            tor->info.pieces[pp].dnd = dnd;
2236    }
2237}
2238
2239void
2240tr_torrentInitFileDLs (tr_torrent             * tor,
2241                       const tr_file_index_t  * files,
2242                       tr_file_index_t          fileCount,
2243                       bool                     doDownload)
2244{
2245    tr_file_index_t i;
2246
2247    assert (tr_isTorrent (tor));
2248
2249    tr_torrentLock (tor);
2250
2251    for (i=0; i<fileCount; ++i)
2252        if (files[i] < tor->info.fileCount)
2253            setFileDND (tor, files[i], doDownload);
2254
2255    tr_cpInvalidateDND (&tor->completion);
2256
2257    tr_torrentUnlock (tor);
2258}
2259
2260void
2261tr_torrentSetFileDLs (tr_torrent             * tor,
2262                      const tr_file_index_t  * files,
2263                      tr_file_index_t          fileCount,
2264                      bool                     doDownload)
2265{
2266    assert (tr_isTorrent (tor));
2267    tr_torrentLock (tor);
2268
2269    tr_torrentInitFileDLs (tor, files, fileCount, doDownload);
2270    tr_torrentSetDirty (tor);
2271    tr_torrentRecheckCompleteness (tor);
2272    tr_peerMgrRebuildRequests (tor);
2273
2274    tr_torrentUnlock (tor);
2275}
2276
2277/***
2278****
2279***/
2280
2281tr_priority_t
2282tr_torrentGetPriority (const tr_torrent * tor)
2283{
2284    assert (tr_isTorrent (tor));
2285
2286    return tor->bandwidth.priority;
2287}
2288
2289void
2290tr_torrentSetPriority (tr_torrent * tor, tr_priority_t priority)
2291{
2292    assert (tr_isTorrent (tor));
2293    assert (tr_isPriority (priority));
2294
2295    if (tor->bandwidth.priority != priority)
2296    {
2297        tor->bandwidth.priority = priority;
2298
2299        tr_torrentSetDirty (tor);
2300    }
2301}
2302
2303/***
2304****
2305***/
2306
2307void
2308tr_torrentSetPeerLimit (tr_torrent * tor,
2309                        uint16_t     maxConnectedPeers)
2310{
2311    assert (tr_isTorrent (tor));
2312
2313    if (tor->maxConnectedPeers != maxConnectedPeers)
2314    {
2315        tor->maxConnectedPeers = maxConnectedPeers;
2316
2317        tr_torrentSetDirty (tor);
2318    }
2319}
2320
2321uint16_t
2322tr_torrentGetPeerLimit (const tr_torrent * tor)
2323{
2324    assert (tr_isTorrent (tor));
2325
2326    return tor->maxConnectedPeers;
2327}
2328
2329/***
2330****
2331***/
2332
2333void
2334tr_torrentGetBlockLocation (const tr_torrent * tor,
2335                            tr_block_index_t   block,
2336                            tr_piece_index_t * piece,
2337                            uint32_t         * offset,
2338                            uint32_t         * length)
2339{
2340    uint64_t pos = block;
2341    pos *= tor->blockSize;
2342    *piece = pos / tor->info.pieceSize;
2343    *offset = pos - (*piece * tor->info.pieceSize);
2344    *length = tr_torBlockCountBytes (tor, block);
2345}
2346
2347
2348tr_block_index_t
2349_tr_block (const tr_torrent * tor,
2350           tr_piece_index_t   index,
2351           uint32_t           offset)
2352{
2353    tr_block_index_t ret;
2354
2355    assert (tr_isTorrent (tor));
2356
2357    ret = index;
2358    ret *= (tor->info.pieceSize / tor->blockSize);
2359    ret += offset / tor->blockSize;
2360    return ret;
2361}
2362
2363bool
2364tr_torrentReqIsValid (const tr_torrent * tor,
2365                      tr_piece_index_t   index,
2366                      uint32_t           offset,
2367                      uint32_t           length)
2368{
2369    int err = 0;
2370
2371    assert (tr_isTorrent (tor));
2372
2373    if (index >= tor->info.pieceCount)
2374        err = 1;
2375    else if (length < 1)
2376        err = 2;
2377    else if ((offset + length) > tr_torPieceCountBytes (tor, index))
2378        err = 3;
2379    else if (length > MAX_BLOCK_SIZE)
2380        err = 4;
2381    else if (tr_pieceOffset (tor, index, offset, length) > tor->info.totalSize)
2382        err = 5;
2383
2384    if (err) tr_logAddTorDbg (tor, "index %lu offset %lu length %lu err %d\n",
2385                            (unsigned long)index,
2386                            (unsigned long)offset,
2387                            (unsigned long)length,
2388                              err);
2389
2390    return !err;
2391}
2392
2393uint64_t
2394tr_pieceOffset (const tr_torrent * tor,
2395                tr_piece_index_t   index,
2396                uint32_t           offset,
2397                uint32_t           length)
2398{
2399    uint64_t ret;
2400
2401    assert (tr_isTorrent (tor));
2402
2403    ret = tor->info.pieceSize;
2404    ret *= index;
2405    ret += offset;
2406    ret += length;
2407    return ret;
2408}
2409
2410void
2411tr_torGetFileBlockRange (const tr_torrent        * tor,
2412                         const tr_file_index_t     file,
2413                         tr_block_index_t        * first,
2414                         tr_block_index_t        * last)
2415{
2416    const tr_file * f = &tor->info.files[file];
2417    uint64_t offset = f->offset;
2418    *first = offset / tor->blockSize;
2419    if (!f->length)
2420        *last = *first;
2421    else {
2422        offset += f->length - 1;
2423        *last = offset / tor->blockSize;
2424    }
2425}
2426
2427void
2428tr_torGetPieceBlockRange (const tr_torrent        * tor,
2429                          const tr_piece_index_t    piece,
2430                          tr_block_index_t        * first,
2431                          tr_block_index_t        * last)
2432{
2433    uint64_t offset = tor->info.pieceSize;
2434    offset *= piece;
2435    *first = offset / tor->blockSize;
2436    offset += (tr_torPieceCountBytes (tor, piece) - 1);
2437    *last = offset / tor->blockSize;
2438}
2439
2440
2441/***
2442****
2443***/
2444
2445void
2446tr_torrentSetPieceChecked (tr_torrent * tor, tr_piece_index_t pieceIndex)
2447{
2448    assert (tr_isTorrent (tor));
2449    assert (pieceIndex < tor->info.pieceCount);
2450
2451    tor->info.pieces[pieceIndex].timeChecked = tr_time ();
2452}
2453
2454void
2455tr_torrentSetChecked (tr_torrent * tor, time_t when)
2456{
2457    tr_piece_index_t i, n;
2458
2459    assert (tr_isTorrent (tor));
2460
2461    for (i=0, n=tor->info.pieceCount; i!=n; ++i)
2462        tor->info.pieces[i].timeChecked = when;
2463}
2464
2465bool
2466tr_torrentCheckPiece (tr_torrent * tor, tr_piece_index_t pieceIndex)
2467{
2468    const bool pass = tr_ioTestPiece (tor, pieceIndex);
2469
2470    tr_deeplog_tor (tor, "[LAZY] tr_torrentCheckPiece tested piece %zu, pass==%d", (size_t)pieceIndex, (int)pass);
2471    tr_torrentSetHasPiece (tor, pieceIndex, pass);
2472    tr_torrentSetPieceChecked (tor, pieceIndex);
2473    tor->anyDate = tr_time ();
2474    tr_torrentSetDirty (tor);
2475
2476    return pass;
2477}
2478
2479time_t
2480tr_torrentGetFileMTime (const tr_torrent * tor, tr_file_index_t i)
2481{
2482    time_t mtime = 0;
2483    if (!tr_fdFileGetCachedMTime (tor->session, tor->uniqueId, i, &mtime))
2484        tr_torrentFindFile2 (tor, i, NULL, NULL, &mtime);
2485    return mtime;
2486}
2487
2488bool
2489tr_torrentPieceNeedsCheck (const tr_torrent * tor, tr_piece_index_t p)
2490{
2491    uint64_t unused;
2492    tr_file_index_t f;
2493    const tr_info * inf = tr_torrentInfo (tor);
2494
2495    /* if we've never checked this piece, then it needs to be checked */
2496    if (!inf->pieces[p].timeChecked)
2497        return true;
2498
2499    /* If we think we've completed one of the files in this piece,
2500     * but it's been modified since we last checked it,
2501     * then it needs to be rechecked */
2502    tr_ioFindFileLocation (tor, p, 0, &f, &unused);
2503    for (; f < inf->fileCount && pieceHasFile (p, &inf->files[f]); ++f)
2504        if (tr_cpFileIsComplete (&tor->completion, f))
2505            if (tr_torrentGetFileMTime (tor, f) > inf->pieces[p].timeChecked)
2506                return true;
2507
2508    return false;
2509}
2510
2511/***
2512****
2513***/
2514
2515static int
2516compareTrackerByTier (const void * va, const void * vb)
2517{
2518    const tr_tracker_info * a = va;
2519    const tr_tracker_info * b = vb;
2520
2521    /* sort by tier */
2522    if (a->tier != b->tier)
2523        return a->tier - b->tier;
2524
2525    /* get the effects of a stable sort by comparing the two elements' addresses */
2526    return a - b;
2527}
2528
2529bool
2530tr_torrentSetAnnounceList (tr_torrent             * tor,
2531                           const tr_tracker_info  * trackers_in,
2532                           int                      trackerCount)
2533{
2534    int i;
2535    tr_variant metainfo;
2536    bool ok = true;
2537    tr_tracker_info * trackers;
2538
2539    tr_torrentLock (tor);
2540
2541    assert (tr_isTorrent (tor));
2542
2543    /* ensure the trackers' tiers are in ascending order */
2544    trackers = tr_memdup (trackers_in, sizeof (tr_tracker_info) * trackerCount);
2545    qsort (trackers, trackerCount, sizeof (tr_tracker_info), compareTrackerByTier);
2546
2547    /* look for bad URLs */
2548    for (i=0; ok && i<trackerCount; ++i)
2549        if (!tr_urlIsValidTracker (trackers[i].announce))
2550            ok = false;
2551
2552    /* save to the .torrent file */
2553    if (ok && !tr_variantFromFile (&metainfo, TR_VARIANT_FMT_BENC, tor->info.torrent))
2554    {
2555        bool hasInfo;
2556        tr_info tmpInfo;
2557
2558        /* remove the old fields */
2559        tr_variantDictRemove (&metainfo, TR_KEY_announce);
2560        tr_variantDictRemove (&metainfo, TR_KEY_announce_list);
2561
2562        /* add the new fields */
2563        if (trackerCount > 0)
2564        {
2565            tr_variantDictAddStr (&metainfo, TR_KEY_announce, trackers[0].announce);
2566        }
2567        if (trackerCount > 1)
2568        {
2569            int i;
2570            int prevTier = -1;
2571            tr_variant * tier = NULL;
2572            tr_variant * announceList = tr_variantDictAddList (&metainfo, TR_KEY_announce_list, 0);
2573
2574            for (i=0; i<trackerCount; ++i) {
2575                if (prevTier != trackers[i].tier) {
2576                    prevTier = trackers[i].tier;
2577                    tier = tr_variantListAddList (announceList, 0);
2578                }
2579                tr_variantListAddStr (tier, trackers[i].announce);
2580            }
2581        }
2582
2583        /* try to parse it back again, to make sure it's good */
2584        memset (&tmpInfo, 0, sizeof (tr_info));
2585        if (tr_metainfoParse (tor->session, &metainfo, &tmpInfo,
2586                              &hasInfo, &tor->infoDictLength))
2587        {
2588            /* it's good, so keep these new trackers and free the old ones */
2589
2590            tr_info swap;
2591            swap.trackers = tor->info.trackers;
2592            swap.trackerCount = tor->info.trackerCount;
2593            tor->info.trackers = tmpInfo.trackers;
2594            tor->info.trackerCount = tmpInfo.trackerCount;
2595            tmpInfo.trackers = swap.trackers;
2596            tmpInfo.trackerCount = swap.trackerCount;
2597
2598            tr_metainfoFree (&tmpInfo);
2599            tr_variantToFile (&metainfo, TR_VARIANT_FMT_BENC, tor->info.torrent);
2600        }
2601
2602        /* cleanup */
2603        tr_variantFree (&metainfo);
2604
2605        /* if we had a tracker-related error on this torrent,
2606         * and that tracker's been removed,
2607         * then clear the error */
2608        if ((tor->error == TR_STAT_TRACKER_WARNING)
2609            || (tor->error == TR_STAT_TRACKER_ERROR))
2610        {
2611            bool clear = true;
2612
2613            for (i=0; clear && i<trackerCount; ++i)
2614                if (!strcmp (trackers[i].announce, tor->errorTracker))
2615                    clear = false;
2616
2617            if (clear)
2618                tr_torrentClearError (tor);
2619        }
2620
2621        /* tell the announcer to reload this torrent's tracker list */
2622        tr_announcerResetTorrent (tor->session->announcer, tor);
2623    }
2624
2625    tr_torrentUnlock (tor);
2626
2627    tr_free (trackers);
2628    return ok;
2629}
2630
2631/**
2632***
2633**/
2634
2635void
2636tr_torrentSetAddedDate (tr_torrent * tor,
2637                        time_t       t)
2638{
2639    assert (tr_isTorrent (tor));
2640
2641    tor->addedDate = t;
2642    tor->anyDate = MAX (tor->anyDate, tor->addedDate);
2643}
2644
2645void
2646tr_torrentSetActivityDate (tr_torrent * tor, time_t t)
2647{
2648    assert (tr_isTorrent (tor));
2649
2650    tor->activityDate = t;
2651    tor->anyDate = MAX (tor->anyDate, tor->activityDate);
2652}
2653
2654void
2655tr_torrentSetDoneDate (tr_torrent * tor,
2656                       time_t       t)
2657{
2658    assert (tr_isTorrent (tor));
2659
2660    tor->doneDate = t;
2661    tor->anyDate = MAX (tor->anyDate, tor->doneDate);
2662}
2663
2664/**
2665***
2666**/
2667
2668uint64_t
2669tr_torrentGetBytesLeftToAllocate (const tr_torrent * tor)
2670{
2671    tr_file_index_t i;
2672    uint64_t bytesLeft = 0;
2673
2674    assert (tr_isTorrent (tor));
2675
2676    for (i=0; i<tor->info.fileCount; ++i)
2677    {
2678        if (!tor->info.files[i].dnd)
2679        {
2680            struct stat sb;
2681            const uint64_t length = tor->info.files[i].length;
2682            char * path = tr_torrentFindFile (tor, i);
2683
2684            bytesLeft += length;
2685
2686            if ((path != NULL) && !stat (path, &sb)
2687                                 && S_ISREG (sb.st_mode)
2688                                 && ((uint64_t)sb.st_size <= length))
2689                bytesLeft -= sb.st_size;
2690
2691            tr_free (path);
2692        }
2693    }
2694
2695    return bytesLeft;
2696}
2697
2698/****
2699*****  Removing the torrent's local data
2700****/
2701
2702static bool
2703isJunkFile (const char * base)
2704{
2705    int i;
2706    static const char * files[] = { ".DS_Store", "desktop.ini", "Thumbs.db" };
2707    static const int file_count = sizeof (files) / sizeof (files[0]);
2708
2709    for (i=0; i<file_count; ++i)
2710        if (!strcmp (base, files[i]))
2711            return true;
2712
2713#ifdef SYS_DARWIN
2714    /* check for resource forks. <http://support.apple.com/kb/TA20578> */
2715    if (!memcmp (base, "._", 2))
2716        return true;
2717#endif
2718
2719    return false;
2720}
2721
2722static void
2723removeEmptyFoldersAndJunkFiles (const char * folder)
2724{
2725    DIR * odir;
2726    if ((odir = opendir (folder))) {
2727        struct dirent * d;
2728        while ((d = readdir (odir))) {
2729            if (strcmp (d->d_name, ".") && strcmp (d->d_name, "..")) {
2730                struct stat sb;
2731                char * filename = tr_buildPath (folder, d->d_name, NULL);
2732                if (!stat (filename, &sb) && S_ISDIR (sb.st_mode))
2733                    removeEmptyFoldersAndJunkFiles (filename);
2734                else if (isJunkFile (d->d_name))
2735                    remove (filename);
2736                tr_free (filename);
2737            }
2738        }
2739        remove (folder);
2740        closedir (odir);
2741    }
2742}
2743
2744/**
2745 * This convoluted code does something (seemingly) simple:
2746 * remove the torrent's local files.
2747 *
2748 * Fun complications:
2749 * 1. Try to preserve the directory hierarchy in the recycle bin.
2750 * 2. If there are nontorrent files, don't delete them...
2751 * 3. ...unless the other files are "junk", such as .DS_Store
2752 */
2753static void
2754deleteLocalData (tr_torrent * tor, tr_fileFunc func)
2755{
2756    int i, n;
2757    tr_file_index_t f;
2758    char * base;
2759    DIR * odir;
2760    char * tmpdir = NULL;
2761    tr_ptrArray files = TR_PTR_ARRAY_INIT;
2762    tr_ptrArray folders = TR_PTR_ARRAY_INIT;
2763    const void * const vstrcmp = strcmp;
2764    const char * const top = tor->currentDir;
2765
2766    /* if it's a magnet link, there's nothing to move... */
2767    if (!tr_torrentHasMetadata (tor))
2768        return;
2769
2770    /***
2771    ****  Move the local data to a new tmpdir
2772    ***/
2773
2774    base = tr_strdup_printf ("%s__XXXXXX", tr_torrentName (tor));
2775    tmpdir = tr_buildPath (top, base, NULL);
2776    tr_mkdtemp (tmpdir);
2777    tr_free (base);
2778
2779    for (f=0; f<tor->info.fileCount; ++f)
2780    {
2781        char * filename = tr_buildPath (top, tor->info.files[f].name, NULL);
2782        if (!tr_fileExists (filename, NULL)) {
2783                char * partial = tr_torrentBuildPartial (tor, f);
2784                tr_free (filename);
2785                filename = tr_buildPath (top, partial, NULL);
2786                tr_free (partial);
2787                if (!tr_fileExists (filename, NULL)) {
2788                        tr_free (filename);
2789                        filename = NULL;
2790                }
2791        }
2792
2793        if (filename != NULL)
2794        {
2795            char * target = tr_buildPath (tmpdir, tor->info.files[f].name, NULL);
2796            tr_moveFile (filename, target, NULL);
2797            tr_ptrArrayAppend (&files, target);
2798            tr_free (filename);
2799        }
2800    }
2801
2802    /***
2803    ****  Remove tmpdir.
2804    ****
2805    ****  Try deleting the top-level files & folders to preserve
2806    ****  the directory hierarchy in the recycle bin.
2807    ****  If case that fails -- for example, rmdir () doesn't
2808    ****  delete nonempty folders -- go from the bottom up too.
2809    ***/
2810
2811    /* try deleting the local data's top-level files & folders */
2812    if ((odir = opendir (tmpdir)))
2813    {
2814        struct dirent * d;
2815        while ((d = readdir (odir)))
2816        {
2817            if (strcmp (d->d_name, ".") && strcmp (d->d_name, ".."))
2818            {
2819                char * file = tr_buildPath (tmpdir, d->d_name, NULL);
2820                func (file);
2821                tr_free (file);
2822            }
2823        }
2824        closedir (odir);
2825    }
2826
2827    /* go from the bottom up */
2828    for (i=0, n=tr_ptrArraySize (&files); i<n; ++i)
2829    {
2830        char * walk = tr_strdup (tr_ptrArrayNth (&files, i));
2831        while (tr_fileExists (walk, NULL) && !tr_is_same_file (tmpdir, walk))
2832        {
2833            char * tmp = tr_dirname (walk);
2834            func (walk);
2835            tr_free (walk);
2836            walk = tmp;
2837        }
2838        tr_free (walk);
2839    }
2840
2841    /***
2842    ****  The local data has been removed.
2843    ****  What's left in top are empty folders, junk, and user-generated files.
2844    ****  Remove the first two categories and leave the third.
2845    ***/
2846
2847    /* build a list of 'top's child directories that belong to this torrent */
2848    for (f=0; f<tor->info.fileCount; ++f)
2849    {
2850        /* get the directory that this file goes in... */
2851        char * filename = tr_buildPath (top, tor->info.files[f].name, NULL);
2852        char * dir = tr_dirname (filename);
2853        if (!tr_is_same_file (top, dir) && strcmp (top, dir)) {
2854            for (;;) {
2855                char * parent = tr_dirname (dir);
2856                const bool done_walking = tr_is_same_file (top, parent) || !strcmp (top, parent);
2857                if (done_walking && tr_ptrArrayFindSorted (&folders, dir, vstrcmp))
2858                  tr_ptrArrayInsertSorted (&folders, dir, vstrcmp); /* folders assumes ownership of dir */
2859                else
2860                  tr_free (dir);
2861                dir = parent;
2862                if (done_walking)
2863                  break;
2864            }
2865        }
2866        tr_free (dir);
2867        tr_free (filename);
2868    }
2869    for (i=0, n=tr_ptrArraySize (&folders); i<n; ++i)
2870        removeEmptyFoldersAndJunkFiles (tr_ptrArrayNth (&folders, i));
2871
2872    /* cleanup */
2873    rmdir (tmpdir);
2874    tr_free (tmpdir);
2875    tr_ptrArrayDestruct (&folders, tr_free);
2876    tr_ptrArrayDestruct (&files, tr_free);
2877}
2878
2879static void
2880tr_torrentDeleteLocalData (tr_torrent * tor, tr_fileFunc func)
2881{
2882    assert (tr_isTorrent (tor));
2883
2884    if (func == NULL)
2885        func = remove;
2886
2887    /* close all the files because we're about to delete them */
2888    tr_cacheFlushTorrent (tor->session->cache, tor);
2889    tr_fdTorrentClose (tor->session, tor->uniqueId);
2890
2891    deleteLocalData (tor, func);
2892}
2893
2894/***
2895****
2896***/
2897
2898struct LocationData
2899{
2900    bool move_from_old_location;
2901    volatile int * setme_state;
2902    volatile double * setme_progress;
2903    char * location;
2904    tr_torrent * tor;
2905};
2906
2907static void
2908setLocation (void * vdata)
2909{
2910    bool err = false;
2911    struct LocationData * data = vdata;
2912    tr_torrent * tor = data->tor;
2913    const bool do_move = data->move_from_old_location;
2914    const char * location = data->location;
2915    double bytesHandled = 0;
2916
2917    assert (tr_isTorrent (tor));
2918
2919    tr_logAddDebug ("Moving \"%s\" location from currentDir \"%s\" to \"%s\"",
2920            tr_torrentName (tor), tor->currentDir, location);
2921
2922    tr_mkdirp (location, 0777);
2923
2924    if (!tr_is_same_file (location, tor->currentDir))
2925    {
2926        tr_file_index_t i;
2927
2928        /* bad idea to move files while they're being verified... */
2929        tr_verifyRemove (tor);
2930
2931        /* try to move the files.
2932         * FIXME: there are still all kinds of nasty cases, like what
2933         * if the target directory runs out of space halfway through... */
2934        for (i=0; !err && i<tor->info.fileCount; ++i)
2935        {
2936            const tr_file * f = &tor->info.files[i];
2937            const char * oldbase;
2938            char * sub;
2939            if (tr_torrentFindFile2 (tor, i, &oldbase, &sub, NULL))
2940            {
2941                char * oldpath = tr_buildPath (oldbase, sub, NULL);
2942                char * newpath = tr_buildPath (location, sub, NULL);
2943
2944                tr_logAddDebug ("Found file #%d: %s", (int)i, oldpath);
2945
2946                if (do_move && !tr_is_same_file (oldpath, newpath))
2947                {
2948                    bool renamed = false;
2949                    errno = 0;
2950                    tr_logAddTorInfo (tor, "moving \"%s\" to \"%s\"", oldpath, newpath);
2951                    if (tr_moveFile (oldpath, newpath, &renamed))
2952                    {
2953                        err = true;
2954                        tr_logAddTorErr (tor, "error moving \"%s\" to \"%s\": %s",
2955                                        oldpath, newpath, tr_strerror (errno));
2956                    }
2957                }
2958
2959                tr_free (newpath);
2960                tr_free (oldpath);
2961                tr_free (sub);
2962            }
2963
2964            if (data->setme_progress)
2965            {
2966                bytesHandled += f->length;
2967                *data->setme_progress = bytesHandled / tor->info.totalSize;
2968            }
2969        }
2970
2971        if (!err)
2972        {
2973            /* blow away the leftover subdirectories in the old location */
2974            if (do_move)
2975              tr_torrentDeleteLocalData (tor, remove);
2976
2977            /* set the new location and reverify */
2978            tr_torrentSetDownloadDir (tor, location);
2979        }
2980    }
2981
2982    if (!err && do_move)
2983    {
2984        tr_free (tor->incompleteDir);
2985        tor->incompleteDir = NULL;
2986        tor->currentDir = tor->downloadDir;
2987    }
2988
2989    if (data->setme_state)
2990        *data->setme_state = err ? TR_LOC_ERROR : TR_LOC_DONE;
2991
2992    /* cleanup */
2993    tr_free (data->location);
2994    tr_free (data);
2995}
2996
2997void
2998tr_torrentSetLocation (tr_torrent       * tor,
2999                       const char       * location,
3000                       bool               move_from_old_location,
3001                       volatile double  * setme_progress,
3002                       volatile int     * setme_state)
3003{
3004    struct LocationData * data;
3005
3006    assert (tr_isTorrent (tor));
3007
3008    if (setme_state)
3009        *setme_state = TR_LOC_MOVING;
3010    if (setme_progress)
3011        *setme_progress = 0;
3012
3013    /* run this in the libtransmission thread */
3014    data = tr_new (struct LocationData, 1);
3015    data->tor = tor;
3016    data->location = tr_strdup (location);
3017    data->move_from_old_location = move_from_old_location;
3018    data->setme_state = setme_state;
3019    data->setme_progress = setme_progress;
3020    tr_runInEventThread (tor->session, setLocation, data);
3021}
3022
3023/***
3024****
3025***/
3026
3027static void
3028tr_torrentFileCompleted (tr_torrent * tor, tr_file_index_t fileIndex)
3029{
3030    char * sub;
3031    const char * base;
3032    const tr_info * inf = &tor->info;
3033    const tr_file * f = &inf->files[fileIndex];
3034    tr_piece * p;
3035    const tr_piece * pend;
3036    const time_t now = tr_time ();
3037
3038    /* close the file so that we can reopen in read-only mode as needed */
3039    tr_cacheFlushFile (tor->session->cache, tor, fileIndex);
3040    tr_fdFileClose (tor->session, tor, fileIndex);
3041
3042    /* now that the file is complete and closed, we can start watching its
3043     * mtime timestamp for changes to know if we need to reverify pieces */
3044    for (p=&inf->pieces[f->firstPiece], pend=&inf->pieces[f->lastPiece]; p!=pend; ++p)
3045        p->timeChecked = now;
3046
3047    /* if the torrent's current filename isn't the same as the one in the
3048     * metadata -- for example, if it had the ".part" suffix appended to
3049     * it until now -- then rename it to match the one in the metadata */
3050    if (tr_torrentFindFile2 (tor, fileIndex, &base, &sub, NULL))
3051    {
3052        if (strcmp (sub, f->name))
3053        {
3054            char * oldpath = tr_buildPath (base, sub, NULL);
3055            char * newpath = tr_buildPath (base, f->name, NULL);
3056
3057            if (rename (oldpath, newpath))
3058                tr_logAddTorErr (tor, "Error moving \"%s\" to \"%s\": %s", oldpath, newpath, tr_strerror (errno));
3059
3060            tr_free (newpath);
3061            tr_free (oldpath);
3062        }
3063
3064        tr_free (sub);
3065    }
3066}
3067
3068static void
3069tr_torrentPieceCompleted (tr_torrent * tor, tr_piece_index_t pieceIndex)
3070{
3071  tr_file_index_t i;
3072
3073  tr_peerMgrPieceCompleted (tor, pieceIndex);
3074
3075  /* if this piece completes any file, invoke the fileCompleted func for it */
3076  for (i=0; i<tor->info.fileCount; ++i)
3077    {
3078      const tr_file * file = &tor->info.files[i];
3079
3080      if ((file->firstPiece <= pieceIndex) && (pieceIndex <= file->lastPiece))
3081        if (tr_cpFileIsComplete (&tor->completion, i))
3082          tr_torrentFileCompleted (tor, i);
3083    }
3084}
3085
3086void
3087tr_torrentGotBlock (tr_torrent * tor, tr_block_index_t block)
3088{
3089  const bool block_is_new = !tr_cpBlockIsComplete (&tor->completion, block);
3090
3091  assert (tr_isTorrent (tor));
3092  assert (tr_amInEventThread (tor->session));
3093
3094  if (block_is_new)
3095    {
3096      tr_piece_index_t p;
3097
3098      tr_cpBlockAdd (&tor->completion, block);
3099      tr_torrentSetDirty (tor);
3100
3101      p = tr_torBlockPiece (tor, block);
3102      if (tr_cpPieceIsComplete (&tor->completion, p))
3103        {
3104          tr_logAddTorDbg (tor, "[LAZY] checking just-completed piece %zu", (size_t)p);
3105
3106          if (tr_torrentCheckPiece (tor, p))
3107            {
3108              tr_torrentPieceCompleted (tor, p);
3109            }
3110          else
3111            {
3112              const uint32_t n = tr_torPieceCountBytes (tor, p);
3113              tr_logAddTorErr (tor, _("Piece %"PRIu32", which was just downloaded, failed its checksum test"), p);
3114              tor->corruptCur += n;
3115              tor->downloadedCur -= MIN (tor->downloadedCur, n);
3116              tr_peerMgrGotBadPiece (tor, p);
3117            }
3118        }
3119    }
3120  else
3121    {
3122      const uint32_t n = tr_torBlockCountBytes (tor, block);
3123      tor->downloadedCur -= MIN (tor->downloadedCur, n);
3124      tr_logAddTorDbg (tor, "we have this block already...");
3125    }
3126}
3127
3128/***
3129****
3130***/
3131
3132bool
3133tr_torrentFindFile2 (const tr_torrent * tor, tr_file_index_t fileNum,
3134                     const char ** base, char ** subpath, time_t * mtime)
3135{
3136    char * part = NULL;
3137    const tr_file * file;
3138    const char * b = NULL;
3139    const char * s = NULL;
3140
3141    assert (tr_isTorrent (tor));
3142    assert (fileNum < tor->info.fileCount);
3143
3144    file = &tor->info.files[fileNum];
3145
3146    if (b == NULL) {
3147        char * filename = tr_buildPath (tor->downloadDir, file->name, NULL);
3148        if (tr_fileExists (filename, mtime)) {
3149            b = tor->downloadDir;
3150            s = file->name;
3151        }
3152        tr_free (filename);
3153    }
3154
3155    if ((b == NULL) && (tor->incompleteDir != NULL)) {
3156        char * filename = tr_buildPath (tor->incompleteDir, file->name, NULL);
3157        if (tr_fileExists (filename, mtime)) {
3158            b = tor->incompleteDir;
3159            s = file->name;
3160        }
3161        tr_free (filename);
3162    }
3163
3164    if (b == NULL)
3165        part = tr_torrentBuildPartial (tor, fileNum);
3166
3167    if ((b == NULL) && (tor->incompleteDir != NULL)) {
3168        char * filename = tr_buildPath (tor->incompleteDir, part, NULL);
3169        if (tr_fileExists (filename, mtime)) {
3170            b = tor->incompleteDir;
3171            s = part;
3172        }
3173        tr_free (filename);
3174    }
3175
3176    if (b == NULL) {
3177        char * filename = tr_buildPath (tor->downloadDir, part, NULL);
3178        if (tr_fileExists (filename, mtime)) {
3179            b = tor->downloadDir;
3180            s = part;
3181        }
3182        tr_free (filename);
3183    }
3184
3185    if (base != NULL)
3186        *base = b;
3187    if (subpath != NULL)
3188        *subpath = tr_strdup (s);
3189
3190    tr_free (part);
3191    return b != NULL;
3192}
3193
3194char*
3195tr_torrentFindFile (const tr_torrent * tor, tr_file_index_t fileNum)
3196{
3197    char * subpath;
3198    char * ret = NULL;
3199    const char * base;
3200
3201    if (tr_torrentFindFile2 (tor, fileNum, &base, &subpath, NULL))
3202    {
3203        ret = tr_buildPath (base, subpath, NULL);
3204        tr_free (subpath);
3205    }
3206
3207    return ret;
3208}
3209
3210/* Decide whether we should be looking for files in downloadDir or incompleteDir. */
3211static void
3212refreshCurrentDir (tr_torrent * tor)
3213{
3214    const char * dir = NULL;
3215
3216    if (tor->incompleteDir == NULL)
3217        dir = tor->downloadDir;
3218    else if (!tr_torrentHasMetadata (tor)) /* no files to find */
3219        dir = tor->incompleteDir;
3220    else if (!tr_torrentFindFile2 (tor, 0, &dir, NULL, NULL))
3221        dir = tor->incompleteDir;
3222
3223    assert (dir != NULL);
3224    assert ((dir == tor->downloadDir) || (dir == tor->incompleteDir));
3225    tor->currentDir = dir;
3226}
3227
3228char*
3229tr_torrentBuildPartial (const tr_torrent * tor, tr_file_index_t fileNum)
3230{
3231    return tr_strdup_printf ("%s.part", tor->info.files[fileNum].name);
3232}
3233
3234/***
3235****
3236***/
3237
3238static int
3239compareTorrentByQueuePosition (const void * va, const void * vb)
3240{
3241    const tr_torrent * a = * (const tr_torrent **) va;
3242    const tr_torrent * b = * (const tr_torrent **) vb;
3243
3244    return a->queuePosition - b->queuePosition;
3245}
3246
3247#ifndef NDEBUG
3248static bool
3249queueIsSequenced (tr_session * session)
3250{
3251    int i ;
3252    int n ;
3253    bool is_sequenced = true;
3254    tr_torrent * tor;
3255    tr_torrent ** tmp = tr_new (tr_torrent *, session->torrentCount);
3256
3257    /* get all the torrents */
3258    n = 0;
3259    tor = NULL;
3260    while ((tor = tr_torrentNext (session, tor)))
3261        tmp[n++] = tor;
3262
3263    /* sort them by position */
3264    qsort (tmp, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3265
3266#if 0
3267    fprintf (stderr, "%s", "queue: ");
3268    for (i=0; i<n; ++i)
3269        fprintf (stderr, "%d ", tmp[i]->queuePosition);
3270    fputc ('\n', stderr);
3271#endif
3272
3273    /* test them */
3274    for (i=0; is_sequenced && i<n; ++i)
3275        if (tmp[i]->queuePosition != i)
3276            is_sequenced = false;
3277
3278    tr_free (tmp);
3279    return is_sequenced;
3280}
3281#endif
3282
3283int
3284tr_torrentGetQueuePosition (const tr_torrent * tor)
3285{
3286    return tor->queuePosition;
3287}
3288
3289void
3290tr_torrentSetQueuePosition (tr_torrent * tor, int pos)
3291{
3292    int back = -1;
3293    tr_torrent * walk;
3294    const int old_pos = tor->queuePosition;
3295    const time_t now = tr_time ();
3296
3297    if (pos < 0)
3298        pos = 0;
3299
3300    tor->queuePosition = -1;
3301
3302    walk = NULL;
3303    while ((walk = tr_torrentNext (tor->session, walk)))
3304    {
3305        if (old_pos < pos) {
3306            if ((old_pos <= walk->queuePosition) && (walk->queuePosition <= pos)) {
3307                walk->queuePosition--;
3308                walk->anyDate = now;
3309            }
3310        }
3311
3312        if (old_pos > pos) {
3313            if ((pos <= walk->queuePosition) && (walk->queuePosition < old_pos)) {
3314                walk->queuePosition++;
3315                walk->anyDate = now;
3316            }
3317        }
3318
3319        if (back < walk->queuePosition)
3320            back = walk->queuePosition;
3321    }
3322
3323    tor->queuePosition = MIN (pos, (back+1));
3324    tor->anyDate = now;
3325
3326    assert (queueIsSequenced (tor->session));
3327}
3328
3329void
3330tr_torrentsQueueMoveTop (tr_torrent ** torrents_in, int n)
3331{
3332    int i;
3333    tr_torrent ** torrents = tr_memdup (torrents_in, sizeof (tr_torrent *) * n);
3334    qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3335    for (i=n-1; i>=0; --i)
3336        tr_torrentSetQueuePosition (torrents[i], 0);
3337    tr_free (torrents);
3338}
3339
3340void
3341tr_torrentsQueueMoveUp (tr_torrent ** torrents_in, int n)
3342{
3343    int i;
3344    tr_torrent ** torrents = tr_memdup (torrents_in, sizeof (tr_torrent *) * n);
3345    qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3346    for (i=0; i<n; ++i)
3347        tr_torrentSetQueuePosition (torrents[i], torrents[i]->queuePosition - 1);
3348    tr_free (torrents);
3349}
3350
3351void
3352tr_torrentsQueueMoveDown (tr_torrent ** torrents_in, int n)
3353{
3354    int i;
3355    tr_torrent ** torrents = tr_memdup (torrents_in, sizeof (tr_torrent *) * n);
3356    qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3357    for (i=n-1; i>=0; --i)
3358        tr_torrentSetQueuePosition (torrents[i], torrents[i]->queuePosition + 1);
3359    tr_free (torrents);
3360}
3361
3362void
3363tr_torrentsQueueMoveBottom (tr_torrent ** torrents_in, int n)
3364{
3365    int i;
3366    tr_torrent ** torrents = tr_memdup (torrents_in, sizeof (tr_torrent *) * n);
3367    qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3368    for (i=0; i<n; ++i)
3369        tr_torrentSetQueuePosition (torrents[i], INT_MAX);
3370    tr_free (torrents);
3371}
3372
3373static void
3374torrentSetQueued (tr_torrent * tor, bool queued)
3375{
3376    assert (tr_isTorrent (tor));
3377    assert (tr_isBool (queued));
3378
3379    if (tr_torrentIsQueued (tor) != queued)
3380    {
3381        tor->isQueued = queued;
3382        tor->anyDate = tr_time ();
3383    }
3384}
3385
3386void
3387tr_torrentSetQueueStartCallback (tr_torrent * torrent, void (*callback)(tr_torrent *, void *), void * user_data)
3388{
3389    torrent->queue_started_callback = callback;
3390    torrent->queue_started_user_data = user_data;
3391}
3392
3393
3394/***
3395****
3396****  RENAME
3397****
3398***/
3399
3400static bool
3401renameArgsAreValid (const char * oldpath, const char * newname)
3402{
3403  return (oldpath && *oldpath)
3404      && (newname && *newname)
3405      && (strcmp (newname, "."))
3406      && (strcmp (newname, ".."))
3407      && (strchr (newname, TR_PATH_DELIMITER) == NULL);
3408}
3409
3410static tr_file_index_t *
3411renameFindAffectedFiles (tr_torrent * tor, const char * oldpath, size_t * setme_n)
3412{
3413  size_t n;
3414  size_t oldpath_len;
3415  tr_file_index_t i;
3416  tr_file_index_t * indices = tr_new0 (tr_file_index_t, tor->info.fileCount);
3417
3418  n = 0;
3419  oldpath_len = strlen (oldpath);
3420  for (i=0; i!=tor->info.fileCount; ++i)
3421    {
3422      const char * name = tor->info.files[i].name;
3423      const size_t len = strlen (name);
3424      if ((len >= oldpath_len) && !memcmp (oldpath, name, oldpath_len))
3425        indices[n++] = i;
3426    }
3427
3428  *setme_n = n;
3429  return indices;
3430}
3431
3432static int
3433renamePath (tr_torrent  * tor,
3434            const char  * oldpath,
3435            const char  * newname)
3436{
3437  char * src;
3438  const char * base;
3439  int error = 0;
3440
3441  if (!tr_torrentIsSeed(tor) && (tor->incompleteDir != NULL))
3442    base = tor->incompleteDir;
3443  else
3444    base = tor->downloadDir;
3445
3446  src = tr_buildPath (base, oldpath, NULL);
3447  if (!tr_fileExists (src, NULL)) /* check for it as a partial */
3448    {
3449      char * tmp = tr_strdup_printf ("%s.part", src);
3450      tr_free (src);
3451      src = tmp;
3452    }
3453
3454  if (tr_fileExists (src, NULL))
3455    {
3456      int tmp;
3457      bool tgt_exists;
3458      char * parent = tr_dirname (src);
3459      char * tgt;
3460
3461      if (tr_str_has_suffix (src, ".part"))
3462        tgt = tr_strdup_printf ("%s" TR_PATH_DELIMITER_STR "%s.part", parent, newname);
3463      else
3464        tgt = tr_buildPath (parent, newname, NULL);
3465
3466      tmp = errno;
3467      tgt_exists = tr_fileExists (tgt, NULL);
3468      errno = tmp;
3469
3470      if (!tgt_exists)
3471        {
3472          int rv;
3473
3474          tmp = errno;
3475          rv = rename (src, tgt);
3476          if (rv != 0)
3477            error = errno;
3478          errno = tmp;
3479        }
3480
3481      tr_free (tgt);
3482      tr_free (parent);
3483    }
3484
3485  tr_free (src);
3486
3487  return error;
3488}
3489
3490static void
3491renameTorrentFileString (tr_torrent       * tor,
3492                         const char       * oldpath,
3493                         const char       * newname,
3494                         tr_file_index_t    fileIndex)
3495{
3496  char * name;
3497  tr_file * file = &tor->info.files[fileIndex];
3498  const size_t oldpath_len = strlen (oldpath);
3499
3500  if (strchr (oldpath, TR_PATH_DELIMITER) == NULL)
3501    {
3502      if (oldpath_len >= strlen(file->name))
3503        name = tr_buildPath (newname, NULL);
3504      else
3505        name = tr_buildPath (newname, file->name + oldpath_len + 1, NULL);
3506    }
3507  else
3508    {
3509      char * tmp = tr_dirname (oldpath);
3510
3511      if (oldpath_len >= strlen(file->name))
3512        name = tr_buildPath (tmp, newname, NULL);
3513      else
3514        name = tr_buildPath (tmp, newname, file->name + oldpath_len + 1, NULL);
3515
3516      tr_free (tmp);
3517    }
3518     
3519  if (!strcmp (file->name, name))
3520    {
3521      tr_free (name);
3522    }
3523  else
3524    { 
3525      tr_free (file->name);
3526      file->name = name;
3527      file->is_renamed = true;
3528    }
3529}
3530
3531struct rename_data
3532{
3533  tr_torrent * tor;
3534  char * oldpath;
3535  char * newname;
3536  tr_torrent_rename_done_func * callback;
3537  void * callback_user_data;
3538};
3539
3540static void
3541torrentRenamePath (void * vdata)
3542{
3543  int error = 0;
3544  struct rename_data * data = vdata;
3545  tr_torrent * const tor = data->tor;
3546  const char * const oldpath = data->oldpath;
3547  const char * const newname = data->newname;
3548
3549  /***
3550  ****
3551  ***/
3552
3553  assert (tr_isTorrent (tor));
3554
3555  if (!renameArgsAreValid (oldpath, newname))
3556    {
3557      error = EINVAL;
3558    }
3559  else
3560    {
3561      size_t n;
3562      tr_file_index_t * file_indices;
3563
3564      file_indices = renameFindAffectedFiles (tor, oldpath, &n);
3565      if (n == 0)
3566        {
3567          errno = EINVAL;
3568        }
3569      else
3570        {
3571          size_t i;
3572
3573          error = renamePath (tor, oldpath, newname);
3574
3575          if (!error)
3576            {
3577              /* update tr_info.files */
3578              for (i=0; i<n; ++i)
3579                renameTorrentFileString(tor, oldpath, newname, file_indices[i]);
3580
3581              /* update tr_info.name if user changed the toplevel */
3582              if ((n == tor->info.fileCount) && (strchr(oldpath,'/')==NULL))
3583                {
3584                  tr_free (tor->info.name);
3585                  tor->info.name = tr_strdup (newname);
3586                }
3587
3588              tr_torrentSetDirty (tor);
3589            }
3590        }
3591
3592      tr_free (file_indices);
3593    }
3594
3595
3596  /***
3597  ****
3598  ***/
3599
3600  tor->anyDate = tr_time ();
3601
3602  /* callback */
3603  if (data->callback != NULL)
3604    (*data->callback)(tor, data->oldpath, data->newname, error, data->callback_user_data);
3605
3606  /* cleanup */
3607  tr_free (data->oldpath);
3608  tr_free (data->newname);
3609  tr_free (data);
3610}
3611
3612void
3613tr_torrentRenamePath (tr_torrent                  * tor,
3614                      const char                  * oldpath,
3615                      const char                  * newname,
3616                      tr_torrent_rename_done_func   callback,
3617                      void                        * callback_user_data)
3618{
3619  struct rename_data * data;
3620
3621  data = tr_new0 (struct rename_data, 1);
3622  data->tor = tor;
3623  data->oldpath = tr_strdup (oldpath);
3624  data->newname = tr_strdup (newname);
3625  data->callback = callback;
3626  data->callback_user_data = callback_user_data;
3627
3628  tr_runInEventThread (tor->session, torrentRenamePath, data);
3629}
Note: See TracBrowser for help on using the repository browser.