source: trunk/libtransmission/torrent.c @ 14156

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

(trunk, libT) #5452 'preventing zombies from child scripts' -- fixed.

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