source: trunk/libtransmission/torrent.c @ 14316

Last change on this file since 14316 was 14316, checked in by livings124, 8 years ago

Use built-in _WIN32 macro instead of WIN32

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