source: trunk/libtransmission/torrent.c @ 13834

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

(libT) #1220 'change file and folder names': fold tr_torrentRename() into tr_torrentRenamePath(); it's not useful enough on its own to be a separate function

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