source: trunk/libtransmission/torrent.c @ 13905

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

fix race condition triggered in the unit tests by requiring a libtransmission thread lock in torrentRemove()

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