source: trunk/libtransmission/torrent.c

Last change on this file was 14718, checked in by mikedld, 3 months ago

Explicitly compare result of str(n)cmp/memcmp to signify that it's not boolean

  • Property svn:keywords set to Date Rev Author Id
File size: 94.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 14718 2016-03-13 22:11:01Z mikedld $
8 */
9
10#include <errno.h> /* EINVAL */
11#include <signal.h> /* signal () */
12
13#ifndef _WIN32
14 #include <sys/wait.h> /* wait () */
15 #include <unistd.h> /* fork (), execvp (), _exit () */
16#else
17 #include <windows.h> /* CreateProcess (), GetLastError () */
18#endif
19
20#include <assert.h>
21#include <math.h>
22#include <stdarg.h>
23#include <string.h> /* memcmp */
24#include <stdlib.h> /* qsort */
25#include <limits.h> /* INT_MAX */
26
27#include <event2/util.h> /* evutil_vsnprintf () */
28
29#include "transmission.h"
30#include "announcer.h"
31#include "bandwidth.h"
32#include "cache.h"
33#include "completion.h"
34#include "crypto-utils.h" /* for tr_sha1 */
35#include "error.h"
36#include "fdlimit.h" /* tr_fdTorrentClose */
37#include "file.h"
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 "resume.h"
47#include "session.h"
48#include "torrent.h"
49#include "torrent-magnet.h"
50#include "trevent.h" /* tr_runInEventThread () */
51#include "utils.h"
52#include "variant.h"
53#include "verify.h"
54#include "version.h"
55
56/***
57****
58***/
59
60#define tr_deeplog_tor(tor, ...) \
61  do \
62    { \
63      if (tr_logGetDeepEnabled ()) \
64        tr_logAddDeep (__FILE__, __LINE__, tr_torrentName (tor), __VA_ARGS__); \
65    } \
66  while (0)
67
68/***
69****
70***/
71
72const char *
73tr_torrentName (const tr_torrent * tor)
74{
75  return tor ? tor->info.name : "";
76}
77
78int
79tr_torrentId (const tr_torrent * tor)
80{
81  return tor ? tor->uniqueId : -1;
82}
83
84tr_torrent*
85tr_torrentFindFromId (tr_session * session, int id)
86{
87  tr_torrent * tor = NULL;
88
89  while ((tor = tr_torrentNext (session, tor)))
90    if (tor->uniqueId == id)
91      return tor;
92
93  return NULL;
94}
95
96tr_torrent*
97tr_torrentFindFromHashString (tr_session *  session, const char * str)
98{
99  tr_torrent * tor = NULL;
100
101  while ((tor = tr_torrentNext (session, tor)))
102    if (!evutil_ascii_strcasecmp (str, tor->info.hashString))
103      return tor;
104
105  return NULL;
106}
107
108tr_torrent*
109tr_torrentFindFromHash (tr_session * session, const uint8_t * torrentHash)
110{
111  tr_torrent * tor = NULL;
112
113  while ((tor = tr_torrentNext (session, tor)))
114    if (*tor->info.hash == *torrentHash)
115      if (memcmp (tor->info.hash, torrentHash, SHA_DIGEST_LENGTH) == 0)
116        return tor;
117
118  return NULL;
119}
120
121tr_torrent*
122tr_torrentFindFromMagnetLink (tr_session * session, const char * magnet)
123{
124  tr_magnet_info * info;
125  tr_torrent * tor = NULL;
126
127  if ((info = tr_magnetParse (magnet)))
128    {
129      tor = tr_torrentFindFromHash (session, info->hash);
130      tr_magnetFree (info);
131    }
132
133  return tor;
134}
135
136tr_torrent*
137tr_torrentFindFromObfuscatedHash (tr_session * session,
138                                  const uint8_t * obfuscatedTorrentHash)
139{
140  tr_torrent * tor = NULL;
141
142  while ((tor = tr_torrentNext (session, tor)))
143    if (memcmp (tor->obfuscatedHash, obfuscatedTorrentHash, SHA_DIGEST_LENGTH) == 0)
144      return tor;
145
146  return NULL;
147}
148
149bool
150tr_torrentIsPieceTransferAllowed (const tr_torrent  * tor,
151                                  tr_direction        direction)
152{
153  bool allowed = true;
154  unsigned int limit;
155
156  assert (tr_isTorrent (tor));
157  assert (tr_isDirection (direction));
158
159  if (tr_torrentUsesSpeedLimit (tor, direction))
160    if (tr_torrentGetSpeedLimit_Bps (tor, direction) <= 0)
161      allowed = false;
162
163  if (tr_torrentUsesSessionLimits (tor))
164    if (tr_sessionGetActiveSpeedLimit_Bps (tor->session, direction, &limit))
165      if (limit <= 0)
166        allowed = false;
167
168  return allowed;
169}
170
171/***
172****
173***/
174
175static void
176tr_torrentUnsetPeerId (tr_torrent * tor)
177{
178  /* triggers a rebuild next time tr_torrentGetPeerId() is called */
179  *tor->peer_id = '\0';
180}
181
182static int
183peerIdTTL (const tr_torrent * tor)
184{
185  int ttl;
186
187  if (!tor->peer_id_creation_time)
188    ttl = 0;
189  else
190    ttl = (int)difftime(tor->peer_id_creation_time+(tor->session->peer_id_ttl_hours*3600), tr_time());
191
192  return ttl;
193}
194
195const unsigned char *
196tr_torrentGetPeerId (tr_torrent * tor)
197{
198  bool needs_new_peer_id = false;
199
200  if (!*tor->peer_id)
201    needs_new_peer_id = true;
202
203  if (!needs_new_peer_id)
204    if (!tr_torrentIsPrivate (tor))
205      if (peerIdTTL (tor) <= 0)
206        needs_new_peer_id = true;
207
208  if (needs_new_peer_id)
209    {
210      tr_peerIdInit (tor->peer_id);
211      tor->peer_id_creation_time = tr_time ();
212    }
213
214  return tor->peer_id;
215}
216/***
217****  PER-TORRENT UL / DL SPEEDS
218***/
219
220void
221tr_torrentSetSpeedLimit_Bps (tr_torrent * tor, tr_direction dir, unsigned int Bps)
222{
223  assert (tr_isTorrent (tor));
224  assert (tr_isDirection (dir));
225
226  if (tr_bandwidthSetDesiredSpeed_Bps (&tor->bandwidth, dir, Bps))
227    tr_torrentSetDirty (tor);
228}
229void
230tr_torrentSetSpeedLimit_KBps (tr_torrent * tor, tr_direction dir, unsigned int KBps)
231{
232  tr_torrentSetSpeedLimit_Bps (tor, dir, toSpeedBytes (KBps));
233}
234
235unsigned int
236tr_torrentGetSpeedLimit_Bps (const tr_torrent * tor, tr_direction dir)
237{
238  assert (tr_isTorrent (tor));
239  assert (tr_isDirection (dir));
240
241  return tr_bandwidthGetDesiredSpeed_Bps (&tor->bandwidth, dir);
242}
243unsigned int
244tr_torrentGetSpeedLimit_KBps (const tr_torrent * tor, tr_direction dir)
245{
246  assert (tr_isTorrent (tor));
247  assert (tr_isDirection (dir));
248
249  return toSpeedKBps (tr_torrentGetSpeedLimit_Bps (tor, dir));
250}
251
252void
253tr_torrentUseSpeedLimit (tr_torrent * tor, tr_direction dir, bool do_use)
254{
255  assert (tr_isTorrent (tor));
256  assert (tr_isDirection (dir));
257
258  if (tr_bandwidthSetLimited (&tor->bandwidth, dir, do_use))
259    tr_torrentSetDirty (tor);
260}
261
262bool
263tr_torrentUsesSpeedLimit (const tr_torrent * tor, tr_direction dir)
264{
265  assert (tr_isTorrent (tor));
266
267  return tr_bandwidthIsLimited (&tor->bandwidth, dir);
268}
269
270void
271tr_torrentUseSessionLimits (tr_torrent * tor, bool doUse)
272{
273  bool changed;
274
275  assert (tr_isTorrent (tor));
276
277  changed = tr_bandwidthHonorParentLimits (&tor->bandwidth, TR_UP, doUse);
278  changed |= tr_bandwidthHonorParentLimits (&tor->bandwidth, TR_DOWN, doUse);
279
280  if (changed)
281    tr_torrentSetDirty (tor);
282}
283
284bool
285tr_torrentUsesSessionLimits (const tr_torrent * tor)
286{
287  assert (tr_isTorrent (tor));
288
289  return tr_bandwidthAreParentLimitsHonored (&tor->bandwidth, TR_UP);
290}
291
292/***
293****
294***/
295
296void
297tr_torrentSetRatioMode (tr_torrent *  tor, tr_ratiolimit mode)
298{
299  assert (tr_isTorrent (tor));
300  assert (mode==TR_RATIOLIMIT_GLOBAL || mode==TR_RATIOLIMIT_SINGLE || mode==TR_RATIOLIMIT_UNLIMITED);
301
302  if (mode != tor->ratioLimitMode)
303    {
304      tor->ratioLimitMode = mode;
305
306      tr_torrentSetDirty (tor);
307    }
308}
309
310tr_ratiolimit
311tr_torrentGetRatioMode (const tr_torrent * tor)
312{
313  assert (tr_isTorrent (tor));
314
315  return tor->ratioLimitMode;
316}
317
318void
319tr_torrentSetRatioLimit (tr_torrent * tor, double desiredRatio)
320{
321  assert (tr_isTorrent (tor));
322
323  if ((int)(desiredRatio*100.0) != (int)(tor->desiredRatio*100.0))
324    {
325      tor->desiredRatio = desiredRatio;
326
327      tr_torrentSetDirty (tor);
328    }
329}
330
331double
332tr_torrentGetRatioLimit (const tr_torrent * tor)
333{
334  assert (tr_isTorrent (tor));
335
336  return tor->desiredRatio;
337}
338
339bool
340tr_torrentGetSeedRatio (const tr_torrent * tor, double * ratio)
341{
342  bool isLimited;
343
344  assert (tr_isTorrent (tor));
345
346  switch (tr_torrentGetRatioMode (tor))
347    {
348      case TR_RATIOLIMIT_SINGLE:
349        isLimited = true;
350        if (ratio)
351          *ratio = tr_torrentGetRatioLimit (tor);
352        break;
353
354      case TR_RATIOLIMIT_GLOBAL:
355        isLimited = tr_sessionIsRatioLimited (tor->session);
356        if (isLimited && ratio)
357          *ratio = tr_sessionGetRatioLimit (tor->session);
358        break;
359
360      default: /* TR_RATIOLIMIT_UNLIMITED */
361        isLimited = false;
362        break;
363    }
364
365  return isLimited;
366}
367
368/* returns true if the seed ratio applies --
369 * it applies if the torrent's a seed AND it has a seed ratio set */
370static bool
371tr_torrentGetSeedRatioBytes (const tr_torrent  * tor,
372                             uint64_t          * setmeLeft,
373                             uint64_t          * setmeGoal)
374{
375  double seedRatio;
376  bool seedRatioApplies = false;
377
378  assert (tr_isTorrent (tor));
379
380  if (tr_torrentGetSeedRatio (tor, &seedRatio))
381    {
382      const uint64_t u = tor->uploadedCur + tor->uploadedPrev;
383      const uint64_t d = tor->downloadedCur + tor->downloadedPrev;
384      const uint64_t baseline = d ? d : tr_cpSizeWhenDone (&tor->completion);
385      const uint64_t goal = baseline * seedRatio;
386      if (setmeLeft) *setmeLeft = goal > u ? goal - u : 0;
387      if (setmeGoal) *setmeGoal = goal;
388      seedRatioApplies = tr_torrentIsSeed (tor);
389    }
390
391  return seedRatioApplies;
392}
393
394static bool
395tr_torrentIsSeedRatioDone (const tr_torrent * tor)
396{
397  uint64_t bytesLeft;
398  return tr_torrentGetSeedRatioBytes (tor, &bytesLeft, NULL) && !bytesLeft;
399}
400
401/***
402****
403***/
404
405void
406tr_torrentSetIdleMode (tr_torrent *  tor, tr_idlelimit mode)
407{
408  assert (tr_isTorrent (tor));
409  assert (mode==TR_IDLELIMIT_GLOBAL || mode==TR_IDLELIMIT_SINGLE || mode==TR_IDLELIMIT_UNLIMITED);
410
411  if (mode != tor->idleLimitMode)
412    {
413      tor->idleLimitMode = mode;
414
415      tr_torrentSetDirty (tor);
416    }
417}
418
419tr_idlelimit
420tr_torrentGetIdleMode (const tr_torrent * tor)
421{
422  assert (tr_isTorrent (tor));
423
424  return tor->idleLimitMode;
425}
426
427void
428tr_torrentSetIdleLimit (tr_torrent * tor, uint16_t idleMinutes)
429{
430  assert (tr_isTorrent (tor));
431
432  if (idleMinutes > 0)
433    {
434      tor->idleLimitMinutes = idleMinutes;
435
436      tr_torrentSetDirty (tor);
437    }
438}
439
440uint16_t
441tr_torrentGetIdleLimit (const tr_torrent * tor)
442{
443  assert (tr_isTorrent (tor));
444
445  return tor->idleLimitMinutes;
446}
447
448bool
449tr_torrentGetSeedIdle (const tr_torrent * tor, uint16_t * idleMinutes)
450{
451  bool isLimited;
452
453  switch (tr_torrentGetIdleMode (tor))
454    {
455      case TR_IDLELIMIT_SINGLE:
456        isLimited = true;
457        if (idleMinutes != NULL)
458          *idleMinutes = tr_torrentGetIdleLimit (tor);
459        break;
460
461      case TR_IDLELIMIT_GLOBAL:
462        isLimited = tr_sessionIsIdleLimited (tor->session);
463        if (isLimited && idleMinutes)
464          *idleMinutes = tr_sessionGetIdleLimit (tor->session);
465        break;
466
467      default: /* TR_IDLELIMIT_UNLIMITED */
468        isLimited = false;
469        break;
470    }
471
472  return isLimited;
473}
474
475static bool
476tr_torrentIsSeedIdleLimitDone (tr_torrent * tor)
477{
478  uint16_t idleMinutes;
479  return tr_torrentGetSeedIdle (tor, &idleMinutes)
480    && difftime (tr_time (), MAX (tor->startDate, tor->activityDate)) >= idleMinutes * 60u;
481}
482
483/***
484****
485***/
486
487void
488tr_torrentCheckSeedLimit (tr_torrent * tor)
489{
490  assert (tr_isTorrent (tor));
491
492  if (!tor->isRunning || tor->isStopping || !tr_torrentIsSeed (tor))
493    return;
494
495  /* if we're seeding and reach our seed ratio limit, stop the torrent */
496  if (tr_torrentIsSeedRatioDone (tor))
497    {
498      tr_logAddTorInfo (tor, "%s", "Seed ratio reached; pausing torrent");
499
500      tor->isStopping = true;
501
502      /* maybe notify the client */
503      if (tor->ratio_limit_hit_func != NULL)
504        tor->ratio_limit_hit_func (tor, tor->ratio_limit_hit_func_user_data);
505    }
506  /* if we're seeding and reach our inactiviy limit, stop the torrent */
507  else if (tr_torrentIsSeedIdleLimitDone (tor))
508    {
509      tr_logAddTorInfo (tor, "%s", "Seeding idle limit reached; pausing torrent");
510
511      tor->isStopping = true;
512      tor->finishedSeedingByIdle = true;
513
514      /* maybe notify the client */
515      if (tor->idle_limit_hit_func != NULL)
516        tor->idle_limit_hit_func (tor, tor->idle_limit_hit_func_user_data);
517    }
518}
519
520/***
521****
522***/
523
524void
525tr_torrentSetLocalError (tr_torrent * tor, const char * fmt, ...)
526{
527  va_list ap;
528
529  assert (tr_isTorrent (tor));
530
531  va_start (ap, fmt);
532  tor->error = TR_STAT_LOCAL_ERROR;
533  tor->errorTracker[0] = '\0';
534  evutil_vsnprintf (tor->errorString, sizeof (tor->errorString), fmt, ap);
535  va_end (ap);
536
537  tr_logAddTorErr (tor, "%s", tor->errorString);
538
539  if (tor->isRunning)
540    tor->isStopping = true;
541}
542
543static void
544tr_torrentClearError (tr_torrent * tor)
545{
546  tor->error = TR_STAT_OK;
547  tor->errorString[0] = '\0';
548  tor->errorTracker[0] = '\0';
549}
550
551static void
552onTrackerResponse (tr_torrent * tor, const tr_tracker_event * event, void * unused UNUSED)
553{
554  switch (event->messageType)
555    {
556      case TR_TRACKER_PEERS:
557        {
558          size_t i;
559          const int8_t seedProbability = event->seedProbability;
560          const bool allAreSeeds = seedProbability == 100;
561
562          if (allAreSeeds)
563            tr_logAddTorDbg (tor, "Got %zu seeds from tracker", event->pexCount);
564          else
565            tr_logAddTorDbg (tor, "Got %zu peers from tracker", event->pexCount);
566
567          for (i = 0; i < event->pexCount; ++i)
568            tr_peerMgrAddPex (tor, TR_PEER_FROM_TRACKER, &event->pex[i], seedProbability);
569
570          break;
571        }
572
573      case TR_TRACKER_WARNING:
574        tr_logAddTorErr (tor, _("Tracker warning: \"%s\""), event->text);
575        tor->error = TR_STAT_TRACKER_WARNING;
576        tr_strlcpy (tor->errorTracker, event->tracker, sizeof (tor->errorTracker));
577        tr_strlcpy (tor->errorString, event->text, sizeof (tor->errorString));
578        break;
579
580      case TR_TRACKER_ERROR:
581        tr_logAddTorErr (tor, _("Tracker error: \"%s\""), event->text);
582        tor->error = TR_STAT_TRACKER_ERROR;
583        tr_strlcpy (tor->errorTracker, event->tracker, sizeof (tor->errorTracker));
584        tr_strlcpy (tor->errorString, event->text, sizeof (tor->errorString));
585        break;
586
587      case TR_TRACKER_ERROR_CLEAR:
588        if (tor->error != TR_STAT_LOCAL_ERROR)
589          tr_torrentClearError (tor);
590        break;
591    }
592}
593
594/***
595****
596****  TORRENT INSTANTIATION
597****
598***/
599
600static tr_piece_index_t
601getBytePiece (const tr_info * info, uint64_t byteOffset)
602{
603  tr_piece_index_t piece;
604
605  assert (info);
606  assert (info->pieceSize != 0);
607
608  piece = byteOffset / info->pieceSize;
609
610  /* handle 0-byte files at the end of a torrent */
611  if (byteOffset == info->totalSize)
612    piece = info->pieceCount - 1;
613
614  return piece;
615}
616
617static void
618initFilePieces (tr_info *       info,
619                tr_file_index_t fileIndex)
620{
621  tr_file * file;
622  uint64_t  firstByte, lastByte;
623
624  assert (info);
625  assert (fileIndex < info->fileCount);
626
627  file = &info->files[fileIndex];
628  firstByte = file->offset;
629  lastByte = firstByte + (file->length ? file->length - 1 : 0);
630  file->firstPiece = getBytePiece (info, firstByte);
631  file->lastPiece = getBytePiece (info, lastByte);
632}
633
634static bool
635pieceHasFile (tr_piece_index_t piece,
636              const tr_file *  file)
637{
638  return (file->firstPiece <= piece) && (piece <= file->lastPiece);
639}
640
641static tr_priority_t
642calculatePiecePriority (const tr_torrent * tor,
643                        tr_piece_index_t   piece,
644                        int                fileHint)
645{
646  tr_file_index_t i;
647  tr_priority_t priority = TR_PRI_LOW;
648
649  /* find the first file that has data in this piece */
650  if (fileHint >= 0)
651    {
652      i = fileHint;
653      while (i > 0 && pieceHasFile (piece, &tor->info.files[i - 1]))
654        --i;
655    }
656  else
657    {
658      for (i=0; i<tor->info.fileCount; ++i)
659        if (pieceHasFile (piece, &tor->info.files[i]))
660          break;
661    }
662
663  /* the piece's priority is the max of the priorities
664   * of all the files in that piece */
665  for (; i<tor->info.fileCount; ++i)
666    {
667      const tr_file * file = &tor->info.files[i];
668
669      if (!pieceHasFile (piece, file))
670        break;
671
672      priority = MAX (priority, file->priority);
673
674      /* when dealing with multimedia files, getting the first and
675         last pieces can sometimes allow you to preview it a bit
676         before it's fully downloaded... */
677      if (file->priority >= TR_PRI_NORMAL)
678        if (file->firstPiece == piece || file->lastPiece == piece)
679          priority = TR_PRI_HIGH;
680    }
681
682  return priority;
683}
684
685static void
686tr_torrentInitFilePieces (tr_torrent * tor)
687{
688  int * firstFiles;
689  tr_file_index_t f;
690  tr_piece_index_t p;
691  uint64_t offset = 0;
692  tr_info * inf = &tor->info;
693
694  /* assign the file offsets */
695  for (f=0; f<inf->fileCount; ++f)
696    {
697      inf->files[f].offset = offset;
698      offset += inf->files[f].length;
699      initFilePieces (inf, f);
700    }
701
702  /* build the array of first-file hints to give calculatePiecePriority */
703  firstFiles = tr_new (int, inf->pieceCount);
704  for (p=f=0; p<inf->pieceCount; ++p)
705    {
706      while (inf->files[f].lastPiece < p)
707        ++f;
708      firstFiles[p] = f;
709    }
710
711#if 0
712  /* test to confirm the first-file hints are correct */
713  for (p=0; p<inf->pieceCount; ++p)
714    {
715      f = firstFiles[p];
716      assert (inf->files[f].firstPiece <= p);
717      assert (inf->files[f].lastPiece >= p);
718      if (f > 0)
719        assert (inf->files[f-1].lastPiece < p);
720
721      for (f=0; f<inf->fileCount; ++f)
722        if (pieceHasFile (p, &inf->files[f]))
723          break;
724
725      assert ((int)f == firstFiles[p]);
726    }
727#endif
728
729  for (p=0; p<inf->pieceCount; ++p)
730    inf->pieces[p].priority = calculatePiecePriority (tor, p, firstFiles[p]);
731
732  tr_free (firstFiles);
733}
734
735static void torrentStart (tr_torrent * tor, bool bypass_queue);
736
737/**
738 * Decide on a block size. Constraints:
739 * (1) most clients decline requests over 16 KiB
740 * (2) pieceSize must be a multiple of block size
741 */
742uint32_t
743tr_getBlockSize (uint32_t pieceSize)
744{
745  uint32_t b = pieceSize;
746
747  while (b > MAX_BLOCK_SIZE)
748    b /= 2u;
749
750  if (!b || (pieceSize % b)) /* not cleanly divisible */
751    return 0;
752
753  return b;
754}
755
756static void refreshCurrentDir (tr_torrent * tor);
757
758static void
759torrentInitFromInfo (tr_torrent * tor)
760{
761  uint64_t t;
762  tr_info * info = &tor->info;
763
764  tor->blockSize = tr_getBlockSize (info->pieceSize);
765
766  if (info->pieceSize)
767    tor->lastPieceSize = (uint32_t)(info->totalSize % info->pieceSize);
768
769  if (!tor->lastPieceSize)
770    tor->lastPieceSize = info->pieceSize;
771
772  if (tor->blockSize)
773    tor->lastBlockSize = info->totalSize % tor->blockSize;
774
775  if (!tor->lastBlockSize)
776    tor->lastBlockSize = tor->blockSize;
777
778  tor->blockCount = tor->blockSize
779    ? (info->totalSize + tor->blockSize - 1) / tor->blockSize
780    : 0;
781
782  tor->blockCountInPiece = tor->blockSize
783    ? info->pieceSize / tor->blockSize
784    : 0;
785
786  tor->blockCountInLastPiece = tor->blockSize
787    ? (tor->lastPieceSize + tor->blockSize - 1) / tor->blockSize
788    : 0;
789
790  /* check our work */
791  if (tor->blockSize != 0)
792    assert ((info->pieceSize % tor->blockSize) == 0);
793  t = info->pieceCount - 1;
794  t *= info->pieceSize;
795  t += tor->lastPieceSize;
796  assert (t == info->totalSize);
797  t = tor->blockCount - 1;
798  t *= tor->blockSize;
799  t += tor->lastBlockSize;
800  assert (t == info->totalSize);
801  t = info->pieceCount - 1;
802  t *= tor->blockCountInPiece;
803  t += tor->blockCountInLastPiece;
804  assert (t == (uint64_t)tor->blockCount);
805
806  tr_cpConstruct (&tor->completion, tor);
807
808  tr_torrentInitFilePieces (tor);
809
810  tor->completeness = tr_cpGetStatus (&tor->completion);
811}
812
813static void tr_torrentFireMetadataCompleted (tr_torrent * tor);
814
815void
816tr_torrentGotNewInfoDict (tr_torrent * tor)
817{
818  torrentInitFromInfo (tor);
819
820  tr_peerMgrOnTorrentGotMetainfo (tor);
821
822  tr_torrentFireMetadataCompleted (tor);
823}
824
825static bool
826hasAnyLocalData (const tr_torrent * tor)
827{
828  tr_file_index_t i;
829
830  for (i=0; i<tor->info.fileCount; ++i)
831    if (tr_torrentFindFile2 (tor, i, NULL, NULL, NULL))
832      return true;
833
834  return false;
835}
836
837static bool
838setLocalErrorIfFilesDisappeared (tr_torrent * tor)
839{
840  const bool disappeared = (tr_torrentHaveTotal (tor) > 0) && !hasAnyLocalData (tor);
841
842  if (disappeared)
843    {
844      tr_deeplog_tor (tor, "%s", "[LAZY] uh oh, the files disappeared");
845      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."));
846    }
847
848  return disappeared;
849}
850
851static void
852torrentInit (tr_torrent * tor, const tr_ctor * ctor)
853{
854  bool doStart;
855  uint64_t loaded;
856  const char * dir;
857  bool isNewTorrent;
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 = !tr_sys_path_exists (tor->info.torrent, NULL);
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                  size_t         * 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  size_t 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 == NULL || tor->downloadDir == NULL || strcmp (path, tor->downloadDir) != 0)
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_rand_int_weak (20);
1653  tor->dhtAnnounce6At = now + tr_rand_int_weak (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      tr_sys_path_info info;
1670      char * filename = tr_torrentFindFile (tor, i);
1671
1672      if (filename != NULL && tr_sys_path_get_info (filename, 0, &info, NULL))
1673        byte_count += info.size;
1674
1675      tr_free (filename);
1676    }
1677
1678  return byte_count;
1679}
1680
1681static bool
1682torrentShouldQueue (const tr_torrent * tor)
1683{
1684  const tr_direction dir = tr_torrentGetQueueDirection (tor);
1685
1686  return tr_sessionCountQueueFreeSlots (tor->session, dir) == 0;
1687}
1688
1689static void
1690torrentStart (tr_torrent * tor, bool bypass_queue)
1691{
1692  switch (tr_torrentGetActivity (tor))
1693    {
1694      case TR_STATUS_SEED:
1695      case TR_STATUS_DOWNLOAD:
1696        return; /* already started */
1697        break;
1698
1699      case TR_STATUS_SEED_WAIT:
1700      case TR_STATUS_DOWNLOAD_WAIT:
1701        if (!bypass_queue)
1702          return; /* already queued */
1703        break;
1704
1705      case TR_STATUS_CHECK:
1706      case TR_STATUS_CHECK_WAIT:
1707        /* verifying right now... wait until that's done so
1708         * we'll know what completeness to use/announce */
1709        tor->startAfterVerify = true;
1710        return;
1711        break;
1712
1713      case TR_STATUS_STOPPED:
1714        if (!bypass_queue && torrentShouldQueue (tor))
1715          {
1716            torrentSetQueued (tor, true);
1717            return;
1718          }
1719        break;
1720    }
1721
1722  /* don't allow the torrent to be started if the files disappeared */
1723  if (setLocalErrorIfFilesDisappeared (tor))
1724    return;
1725
1726  /* otherwise, start it now... */
1727  tr_sessionLock (tor->session);
1728
1729  /* allow finished torrents to be resumed */
1730  if (tr_torrentIsSeedRatioDone (tor))
1731    {
1732      tr_logAddTorInfo (tor, "%s", _("Restarted manually -- disabling its seed ratio"));
1733      tr_torrentSetRatioMode (tor, TR_RATIOLIMIT_UNLIMITED);
1734    }
1735
1736  /* corresponds to the peer_id sent as a tracker request parameter.
1737   * one tracker admin says: "When the same torrent is opened and
1738   * closed and opened again without quitting Transmission ...
1739   * change the peerid. It would help sometimes if a stopped event
1740   * was missed to ensure that we didn't think someone was cheating. */
1741  tr_torrentUnsetPeerId (tor);
1742  tor->isRunning = true;
1743  tr_torrentSetDirty (tor);
1744  tr_runInEventThread (tor->session, torrentStartImpl, tor);
1745
1746  tr_sessionUnlock (tor->session);
1747}
1748
1749void
1750tr_torrentStart (tr_torrent * tor)
1751{
1752  if (tr_isTorrent (tor))
1753    torrentStart (tor, false);
1754}
1755
1756void
1757tr_torrentStartNow (tr_torrent * tor)
1758{
1759  if (tr_isTorrent (tor))
1760    torrentStart (tor, true);
1761}
1762
1763struct verify_data
1764{
1765  bool aborted;
1766  tr_torrent * tor;
1767  tr_verify_done_func callback_func;
1768  void * callback_data;
1769};
1770
1771static void
1772onVerifyDoneThreadFunc (void * vdata)
1773{
1774  struct verify_data * data = vdata;
1775  tr_torrent * tor = data->tor;
1776
1777  if (!data->aborted)
1778    tr_torrentRecheckCompleteness (tor);
1779
1780  if (data->callback_func != NULL)
1781    (*data->callback_func)(tor, data->aborted, data->callback_data);
1782
1783  if (!data->aborted && tor->startAfterVerify)
1784    {
1785      tor->startAfterVerify = false;
1786      torrentStart (tor, false);
1787    }
1788
1789  tr_free (data);
1790}
1791
1792static void
1793onVerifyDone (tr_torrent * tor, bool aborted, void * vdata)
1794{
1795  struct verify_data * data = vdata;
1796  assert (data->tor == tor);
1797  data->aborted = aborted;
1798  tr_runInEventThread (tor->session, onVerifyDoneThreadFunc, data);
1799}
1800
1801static void
1802verifyTorrent (void * vdata)
1803{
1804  bool startAfter;
1805  struct verify_data * data = vdata;
1806  tr_torrent * tor = data->tor;
1807  tr_sessionLock (tor->session);
1808
1809  /* if the torrent's already being verified, stop it */
1810  tr_verifyRemove (tor);
1811
1812  startAfter = (tor->isRunning || tor->startAfterVerify) && !tor->isStopping;
1813  if (tor->isRunning)
1814    tr_torrentStop (tor);
1815  tor->startAfterVerify = startAfter;
1816
1817  if (setLocalErrorIfFilesDisappeared (tor))
1818    tor->startAfterVerify = false;
1819  else
1820    tr_verifyAdd (tor, onVerifyDone, data);
1821
1822  tr_sessionUnlock (tor->session);
1823}
1824
1825void
1826tr_torrentVerify (tr_torrent           * tor,
1827                  tr_verify_done_func    callback_func,
1828                  void                 * callback_data)
1829{
1830  struct verify_data * data;
1831
1832  data = tr_new (struct verify_data, 1);
1833  data->tor = tor;
1834  data->aborted = false;
1835  data->callback_func = callback_func;
1836  data->callback_data = callback_data;
1837  tr_runInEventThread (tor->session, verifyTorrent, data);
1838}
1839
1840void
1841tr_torrentSave (tr_torrent * tor)
1842{
1843  assert (tr_isTorrent (tor));
1844
1845  if (tor->isDirty)
1846    {
1847      tor->isDirty = false;
1848      tr_torrentSaveResume (tor);
1849    }
1850}
1851
1852static void
1853stopTorrent (void * vtor)
1854{
1855  tr_torrent * tor = vtor;
1856  tr_logAddTorInfo (tor, "%s", "Pausing");
1857
1858  assert (tr_isTorrent (tor));
1859
1860  tr_torrentLock (tor);
1861
1862  tr_verifyRemove (tor);
1863  tr_peerMgrStopTorrent (tor);
1864  tr_announcerTorrentStopped (tor);
1865  tr_cacheFlushTorrent (tor->session->cache, tor);
1866
1867  tr_fdTorrentClose (tor->session, tor->uniqueId);
1868
1869  if (!tor->isDeleting)
1870    tr_torrentSave (tor);
1871
1872  torrentSetQueued (tor, false);
1873
1874  tr_torrentUnlock (tor);
1875
1876  if (tor->magnetVerify)
1877    {
1878      tor->magnetVerify = false;
1879      tr_logAddTorInfo (tor, "%s", "Magnet Verify");
1880      refreshCurrentDir (tor);
1881      tr_torrentVerify (tor, NULL, NULL);
1882    }
1883}
1884
1885void
1886tr_torrentStop (tr_torrent * tor)
1887{
1888  assert (tr_isTorrent (tor));
1889
1890  if (tr_isTorrent (tor))
1891    {
1892      tr_sessionLock (tor->session);
1893
1894      tor->isRunning = false;
1895      tor->isStopping = false;
1896      tr_torrentSetDirty (tor);
1897      tr_runInEventThread (tor->session, stopTorrent, tor);
1898
1899      tr_sessionUnlock (tor->session);
1900    }
1901}
1902
1903static void
1904closeTorrent (void * vtor)
1905{
1906  tr_variant * d;
1907  tr_torrent * tor = vtor;
1908
1909  assert (tr_isTorrent (tor));
1910
1911  d = tr_variantListAddDict (&tor->session->removedTorrents, 2);
1912  tr_variantDictAddInt (d, TR_KEY_id, tor->uniqueId);
1913  tr_variantDictAddInt (d, TR_KEY_date, tr_time ());
1914
1915  tr_logAddTorInfo (tor, "%s", _("Removing torrent"));
1916
1917  tor->magnetVerify = false;
1918  stopTorrent (tor);
1919
1920  if (tor->isDeleting)
1921    {
1922      tr_metainfoRemoveSaved (tor->session, &tor->info);
1923      tr_torrentRemoveResume (tor);
1924    }
1925
1926  tor->isRunning = false;
1927  freeTorrent (tor);
1928}
1929
1930void
1931tr_torrentFree (tr_torrent * tor)
1932{
1933  if (tr_isTorrent (tor))
1934    {
1935      tr_session * session = tor->session;
1936      assert (tr_isSession (session));
1937      tr_sessionLock (session);
1938
1939      tr_torrentClearCompletenessCallback (tor);
1940      tr_runInEventThread (session, closeTorrent, tor);
1941
1942      tr_sessionUnlock (session);
1943    }
1944}
1945
1946struct remove_data
1947{
1948    tr_torrent  * tor;
1949    bool          deleteFlag;
1950    tr_fileFunc   deleteFunc;
1951};
1952
1953static void tr_torrentDeleteLocalData (tr_torrent *, tr_fileFunc);
1954
1955static void
1956removeTorrent (void * vdata)
1957{
1958  struct remove_data * data = vdata;
1959  tr_session * session = data->tor->session;
1960  tr_sessionLock (session);
1961
1962  if (data->deleteFlag)
1963    tr_torrentDeleteLocalData (data->tor, data->deleteFunc);
1964
1965  tr_torrentClearCompletenessCallback (data->tor);
1966  closeTorrent (data->tor);
1967  tr_free (data);
1968
1969  tr_sessionUnlock (session);
1970}
1971
1972void
1973tr_torrentRemove (tr_torrent   * tor,
1974                  bool           deleteFlag,
1975                  tr_fileFunc    deleteFunc)
1976{
1977  struct remove_data * data;
1978
1979  assert (tr_isTorrent (tor));
1980  tor->isDeleting = true;
1981
1982  data = tr_new0 (struct remove_data, 1);
1983  data->tor = tor;
1984  data->deleteFlag = deleteFlag;
1985  data->deleteFunc = deleteFunc;
1986  tr_runInEventThread (tor->session, removeTorrent, data);
1987}
1988
1989/**
1990***  Completeness
1991**/
1992
1993static const char *
1994getCompletionString (int type)
1995{
1996  switch (type)
1997    {
1998      /* Translators: this is a minor point that's safe to skip over, but FYI:
1999         "Complete" and "Done" are specific, different terms in Transmission:
2000         "Complete" means we've downloaded every file in the torrent.
2001         "Done" means we're done downloading the files we wanted, but NOT all
2002         that exist */
2003      case TR_PARTIAL_SEED:
2004        return _("Done");
2005
2006      case TR_SEED:
2007        return _("Complete");
2008
2009      default:
2010        return _("Incomplete");
2011    }
2012}
2013
2014static void
2015fireCompletenessChange (tr_torrent       * tor,
2016                        tr_completeness    status,
2017                        bool               wasRunning)
2018{
2019  assert ((status == TR_LEECH)
2020       || (status == TR_SEED)
2021       || (status == TR_PARTIAL_SEED));
2022
2023  if (tor->completeness_func)
2024    tor->completeness_func (tor, status, wasRunning,
2025                            tor->completeness_func_user_data);
2026}
2027
2028void
2029tr_torrentSetCompletenessCallback (tr_torrent                    * tor,
2030                                   tr_torrent_completeness_func    func,
2031                                   void                          * user_data)
2032{
2033  assert (tr_isTorrent (tor));
2034
2035  tor->completeness_func = func;
2036  tor->completeness_func_user_data = user_data;
2037}
2038
2039void
2040tr_torrentClearCompletenessCallback (tr_torrent * torrent)
2041{
2042  tr_torrentSetCompletenessCallback (torrent, NULL, NULL);
2043}
2044
2045void
2046tr_torrentSetRatioLimitHitCallback (tr_torrent                     * tor,
2047                                    tr_torrent_ratio_limit_hit_func  func,
2048                                    void                           * user_data)
2049{
2050  assert (tr_isTorrent (tor));
2051
2052  tor->ratio_limit_hit_func = func;
2053  tor->ratio_limit_hit_func_user_data = user_data;
2054}
2055
2056void
2057tr_torrentClearRatioLimitHitCallback (tr_torrent * torrent)
2058{
2059  tr_torrentSetRatioLimitHitCallback (torrent, NULL, NULL);
2060}
2061
2062void
2063tr_torrentSetIdleLimitHitCallback (tr_torrent                    * tor,
2064                                   tr_torrent_idle_limit_hit_func  func,
2065                                   void                          * user_data)
2066{
2067  assert (tr_isTorrent (tor));
2068
2069  tor->idle_limit_hit_func = func;
2070  tor->idle_limit_hit_func_user_data = user_data;
2071}
2072
2073void
2074tr_torrentClearIdleLimitHitCallback (tr_torrent * torrent)
2075{
2076  tr_torrentSetIdleLimitHitCallback (torrent, NULL, NULL);
2077}
2078
2079#ifndef _WIN32
2080
2081static void
2082onSigCHLD (int i UNUSED)
2083{
2084  int rc;
2085  do
2086    rc = waitpid (-1, NULL, WNOHANG);
2087  while (rc>0 || (rc==-1 && errno==EINTR));
2088}
2089
2090#endif
2091
2092static void
2093torrentCallScript (const tr_torrent * tor, const char * script)
2094{
2095  char timeStr[128], * newlinePos;
2096  const time_t now = tr_time ();
2097
2098  tr_strlcpy (timeStr, ctime (&now), sizeof (timeStr));
2099
2100  /* ctime () includes '\n', but it's better to be safe */
2101  newlinePos = strchr (timeStr, '\n');
2102  if (newlinePos != NULL)
2103    *newlinePos = '\0';
2104
2105  if (script && *script)
2106    {
2107      size_t i;
2108      char * cmd[] = { tr_strdup (script), NULL };
2109      char * env[] = {
2110        tr_strdup_printf ("TR_APP_VERSION=%s", SHORT_VERSION_STRING),
2111        tr_strdup_printf ("TR_TIME_LOCALTIME=%s", timeStr),
2112        tr_strdup_printf ("TR_TORRENT_DIR=%s", tor->currentDir),
2113        tr_strdup_printf ("TR_TORRENT_HASH=%s", tor->info.hashString),
2114        tr_strdup_printf ("TR_TORRENT_ID=%d", tr_torrentId (tor)),
2115        tr_strdup_printf ("TR_TORRENT_NAME=%s", tr_torrentName (tor)),
2116        NULL };
2117
2118      tr_logAddTorInfo (tor, "Calling script \"%s\"", script);
2119
2120#ifndef NDEBUG
2121      /* Win32 environment block strings should be sorted alphabetically */
2122      for (i = 1; env[i] != NULL; ++i)
2123        assert (strcmp (env[i - 1], env[i]) < 0);
2124#endif
2125
2126#ifdef _WIN32
2127
2128      wchar_t * wide_script = tr_win32_utf8_to_native (script, -1);
2129
2130      size_t env_block_size = 0;
2131      char * env_block = NULL;
2132      for (i = 0; env[i] != NULL; ++i)
2133        {
2134          const size_t len = strlen (env[i]) + 1;
2135          env_block = tr_renew (char, env_block, env_block_size + len + 1);
2136          memcpy (env_block + env_block_size, env[i], len + 1);
2137          env_block_size += len;
2138        }
2139
2140      wchar_t * wide_env_block = NULL;
2141      if (env_block != NULL)
2142        {
2143          env_block[env_block_size] = '\0';
2144          wide_env_block = tr_win32_utf8_to_native (env_block, env_block_size + 1);
2145          tr_free (env_block);
2146        }
2147
2148      STARTUPINFOW si = { 0, };
2149      si.cb = sizeof (si);
2150      si.dwFlags = STARTF_USESHOWWINDOW;
2151      si.wShowWindow = SW_HIDE;
2152
2153      PROCESS_INFORMATION pi;
2154
2155      if (CreateProcessW (wide_script, NULL, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS |
2156                          CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW | CREATE_DEFAULT_ERROR_MODE |
2157                          DETACHED_PROCESS, wide_env_block, L"\\", &si, &pi))
2158        {
2159          CloseHandle (pi.hThread);
2160          CloseHandle (pi.hProcess);
2161        }
2162      else
2163        {
2164          char * const message = tr_win32_format_message (GetLastError ());
2165          tr_logAddTorErr (tor, "error executing script \"%s\": %s", script, message);
2166          tr_free (message);
2167        }
2168
2169      tr_free (wide_env_block);
2170      tr_free (wide_script);
2171
2172#else /* _WIN32 */
2173
2174      signal (SIGCHLD, onSigCHLD);
2175
2176      if (!fork ())
2177        {
2178          for (i = 0; env[i] != NULL; ++i)
2179            putenv (env[i]);
2180
2181          if (chdir ("/") == -1)
2182            {
2183              /* ignore (nice to have but not that critical) */
2184            }
2185
2186          if (execvp (script, cmd) == -1)
2187            tr_logAddTorErr (tor, "error executing script \"%s\": %s", script, tr_strerror (errno));
2188
2189          _exit (0);
2190        }
2191
2192#endif /* _WIN32 */
2193
2194      for (i = 0; cmd[i] != NULL; ++i)
2195        tr_free (cmd[i]);
2196      for (i = 0; env[i] != NULL; ++i)
2197        tr_free (env[i]);
2198    }
2199}
2200
2201void
2202tr_torrentRecheckCompleteness (tr_torrent * tor)
2203{
2204  tr_completeness completeness;
2205
2206  tr_torrentLock (tor);
2207
2208  completeness = tr_cpGetStatus (&tor->completion);
2209  if (completeness != tor->completeness)
2210    {
2211      const bool recentChange = tor->downloadedCur != 0;
2212      const bool wasLeeching = !tr_torrentIsSeed (tor);
2213      const bool wasRunning = tor->isRunning;
2214
2215      if (recentChange)
2216        tr_logAddTorInfo (tor, _("State changed from \"%1$s\" to \"%2$s\""),
2217                          getCompletionString (tor->completeness),
2218                          getCompletionString (completeness));
2219
2220      tor->completeness = completeness;
2221      tr_fdTorrentClose (tor->session, tor->uniqueId);
2222
2223      if (tr_torrentIsSeed (tor))
2224        {
2225          if (recentChange)
2226            {
2227              tr_announcerTorrentCompleted (tor);
2228              tor->doneDate = tor->anyDate = tr_time ();
2229            }
2230
2231          if (wasLeeching && wasRunning)
2232            {
2233              /* clear interested flag on all peers */
2234              tr_peerMgrClearInterest (tor);
2235            }
2236
2237          if (tor->currentDir == tor->incompleteDir)
2238            tr_torrentSetLocation (tor, tor->downloadDir, true, NULL, NULL);
2239        }
2240
2241      fireCompletenessChange (tor, completeness, wasRunning);
2242
2243      if (tr_torrentIsSeed (tor))
2244        {
2245          if (wasLeeching && wasRunning)
2246            {
2247              /* if completeness was TR_LEECH then the seed limit check will have been skipped in bandwidthPulse */
2248              tr_torrentCheckSeedLimit (tor);
2249            }
2250
2251          if (tr_sessionIsTorrentDoneScriptEnabled (tor->session))
2252            torrentCallScript (tor, tr_sessionGetTorrentDoneScript (tor->session));
2253        }
2254
2255      tr_torrentSetDirty (tor);
2256    }
2257
2258  tr_torrentUnlock (tor);
2259}
2260
2261/***
2262****
2263***/
2264
2265static void
2266tr_torrentFireMetadataCompleted (tr_torrent * tor)
2267{
2268  assert (tr_isTorrent (tor));
2269
2270  if (tor->metadata_func != NULL)
2271    tor->metadata_func (tor, tor->metadata_func_user_data);
2272}
2273
2274void
2275tr_torrentSetMetadataCallback (tr_torrent                * tor,
2276                               tr_torrent_metadata_func    func,
2277                               void                      * user_data)
2278{
2279  assert (tr_isTorrent (tor));
2280
2281  tor->metadata_func = func;
2282  tor->metadata_func_user_data = user_data;
2283}
2284
2285
2286/**
2287***  File priorities
2288**/
2289
2290void
2291tr_torrentInitFilePriority (tr_torrent *    tor,
2292                            tr_file_index_t fileIndex,
2293                            tr_priority_t   priority)
2294{
2295  tr_file * file;
2296  tr_piece_index_t i;
2297
2298  assert (tr_isTorrent (tor));
2299  assert (fileIndex < tor->info.fileCount);
2300  assert (tr_isPriority (priority));
2301
2302  file = &tor->info.files[fileIndex];
2303  file->priority = priority;
2304  for (i=file->firstPiece; i<=file->lastPiece; ++i)
2305    tor->info.pieces[i].priority = calculatePiecePriority (tor, i, fileIndex);
2306}
2307
2308void
2309tr_torrentSetFilePriorities (tr_torrent             * tor,
2310                             const tr_file_index_t  * files,
2311                             tr_file_index_t          fileCount,
2312                             tr_priority_t            priority)
2313{
2314  tr_file_index_t i;
2315  assert (tr_isTorrent (tor));
2316  tr_torrentLock (tor);
2317
2318  for (i=0; i<fileCount; ++i)
2319    if (files[i] < tor->info.fileCount)
2320      tr_torrentInitFilePriority (tor, files[i], priority);
2321  tr_torrentSetDirty (tor);
2322  tr_peerMgrRebuildRequests (tor);
2323
2324  tr_torrentUnlock (tor);
2325}
2326
2327tr_priority_t*
2328tr_torrentGetFilePriorities (const tr_torrent * tor)
2329{
2330  tr_file_index_t i;
2331  tr_priority_t * p;
2332
2333  assert (tr_isTorrent (tor));
2334
2335  p = tr_new0 (tr_priority_t, tor->info.fileCount);
2336
2337  for (i=0; i<tor->info.fileCount; ++i)
2338    p[i] = tor->info.files[i].priority;
2339
2340  return p;
2341}
2342
2343/**
2344***  File DND
2345**/
2346
2347static void
2348setFileDND (tr_torrent * tor, tr_file_index_t fileIndex, int doDownload)
2349{
2350  const int8_t dnd = !doDownload;
2351  tr_piece_index_t firstPiece;
2352  int8_t firstPieceDND;
2353  tr_piece_index_t lastPiece;
2354  int8_t lastPieceDND;
2355  tr_file_index_t  i;
2356  tr_file * file = &tor->info.files[fileIndex];
2357
2358  file->dnd = dnd;
2359  firstPiece = file->firstPiece;
2360  lastPiece = file->lastPiece;
2361
2362  /* can't set the first piece to DND unless
2363     every file using that piece is DND */
2364  firstPieceDND = dnd;
2365  if (fileIndex > 0)
2366    {
2367      for (i=fileIndex-1; firstPieceDND; --i)
2368        {
2369          if (tor->info.files[i].lastPiece != firstPiece)
2370            break;
2371
2372          firstPieceDND = tor->info.files[i].dnd;
2373          if (!i)
2374            break;
2375        }
2376    }
2377
2378  /* can't set the last piece to DND unless
2379     every file using that piece is DND */
2380  lastPieceDND = dnd;
2381  for (i=fileIndex+1; lastPieceDND && i<tor->info.fileCount; ++i)
2382    {
2383      if (tor->info.files[i].firstPiece != lastPiece)
2384        break;
2385      lastPieceDND = tor->info.files[i].dnd;
2386    }
2387
2388  if (firstPiece == lastPiece)
2389    {
2390      tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
2391    }
2392  else
2393    {
2394      tr_piece_index_t pp;
2395      tor->info.pieces[firstPiece].dnd = firstPieceDND;
2396      tor->info.pieces[lastPiece].dnd = lastPieceDND;
2397      for (pp=firstPiece+1; pp<lastPiece; ++pp)
2398        tor->info.pieces[pp].dnd = dnd;
2399    }
2400}
2401
2402void
2403tr_torrentInitFileDLs (tr_torrent             * tor,
2404                       const tr_file_index_t  * files,
2405                       tr_file_index_t          fileCount,
2406                       bool                     doDownload)
2407{
2408  tr_file_index_t i;
2409
2410  assert (tr_isTorrent (tor));
2411
2412  tr_torrentLock (tor);
2413
2414  for (i=0; i<fileCount; ++i)
2415    if (files[i] < tor->info.fileCount)
2416      setFileDND (tor, files[i], doDownload);
2417
2418  tr_cpInvalidateDND (&tor->completion);
2419
2420  tr_torrentUnlock (tor);
2421}
2422
2423void
2424tr_torrentSetFileDLs (tr_torrent             * tor,
2425                      const tr_file_index_t  * files,
2426                      tr_file_index_t          fileCount,
2427                      bool                     doDownload)
2428{
2429  assert (tr_isTorrent (tor));
2430  tr_torrentLock (tor);
2431
2432  tr_torrentInitFileDLs (tor, files, fileCount, doDownload);
2433  tr_torrentSetDirty (tor);
2434  tr_torrentRecheckCompleteness (tor);
2435  tr_peerMgrRebuildRequests (tor);
2436
2437  tr_torrentUnlock (tor);
2438}
2439
2440/***
2441****
2442***/
2443
2444tr_priority_t
2445tr_torrentGetPriority (const tr_torrent * tor)
2446{
2447  assert (tr_isTorrent (tor));
2448
2449  return tor->bandwidth.priority;
2450}
2451
2452void
2453tr_torrentSetPriority (tr_torrent * tor, tr_priority_t priority)
2454{
2455  assert (tr_isTorrent (tor));
2456  assert (tr_isPriority (priority));
2457
2458  if (tor->bandwidth.priority != priority)
2459    {
2460      tor->bandwidth.priority = priority;
2461
2462      tr_torrentSetDirty (tor);
2463    }
2464}
2465
2466/***
2467****
2468***/
2469
2470void
2471tr_torrentSetPeerLimit (tr_torrent * tor,
2472                        uint16_t     maxConnectedPeers)
2473{
2474  assert (tr_isTorrent (tor));
2475
2476  if (tor->maxConnectedPeers != maxConnectedPeers)
2477    {
2478      tor->maxConnectedPeers = maxConnectedPeers;
2479
2480      tr_torrentSetDirty (tor);
2481    }
2482}
2483
2484uint16_t
2485tr_torrentGetPeerLimit (const tr_torrent * tor)
2486{
2487  assert (tr_isTorrent (tor));
2488
2489  return tor->maxConnectedPeers;
2490}
2491
2492/***
2493****
2494***/
2495
2496void
2497tr_torrentGetBlockLocation (const tr_torrent * tor,
2498                            tr_block_index_t   block,
2499                            tr_piece_index_t * piece,
2500                            uint32_t         * offset,
2501                            uint32_t         * length)
2502{
2503  uint64_t pos = block;
2504  pos *= tor->blockSize;
2505  *piece = pos / tor->info.pieceSize;
2506  *offset = pos - (*piece * tor->info.pieceSize);
2507  *length = tr_torBlockCountBytes (tor, block);
2508}
2509
2510
2511tr_block_index_t
2512_tr_block (const tr_torrent * tor,
2513           tr_piece_index_t   index,
2514           uint32_t           offset)
2515{
2516  tr_block_index_t ret;
2517
2518  assert (tr_isTorrent (tor));
2519
2520  ret = index;
2521  ret *= (tor->info.pieceSize / tor->blockSize);
2522  ret += offset / tor->blockSize;
2523  return ret;
2524}
2525
2526bool
2527tr_torrentReqIsValid (const tr_torrent * tor,
2528                      tr_piece_index_t   index,
2529                      uint32_t           offset,
2530                      uint32_t           length)
2531{
2532  int err = 0;
2533
2534  assert (tr_isTorrent (tor));
2535
2536  if (index >= tor->info.pieceCount)
2537    err = 1;
2538  else if (length < 1)
2539    err = 2;
2540  else if ((offset + length) > tr_torPieceCountBytes (tor, index))
2541    err = 3;
2542  else if (length > MAX_BLOCK_SIZE)
2543    err = 4;
2544  else if (tr_pieceOffset (tor, index, offset, length) > tor->info.totalSize)
2545    err = 5;
2546
2547  if (err)
2548    tr_logAddTorDbg (tor, "index %lu offset %lu length %lu err %d\n",
2549                     (unsigned long)index,
2550                     (unsigned long)offset,
2551                     (unsigned long)length,
2552                     err);
2553
2554  return !err;
2555}
2556
2557uint64_t
2558tr_pieceOffset (const tr_torrent * tor,
2559                tr_piece_index_t   index,
2560                uint32_t           offset,
2561                uint32_t           length)
2562{
2563  uint64_t ret;
2564
2565  assert (tr_isTorrent (tor));
2566
2567  ret = tor->info.pieceSize;
2568  ret *= index;
2569  ret += offset;
2570  ret += length;
2571  return ret;
2572}
2573
2574void
2575tr_torGetFileBlockRange (const tr_torrent        * tor,
2576                         const tr_file_index_t     file,
2577                         tr_block_index_t        * first,
2578                         tr_block_index_t        * last)
2579{
2580  const tr_file * f = &tor->info.files[file];
2581  uint64_t offset = f->offset;
2582
2583  *first = offset / tor->blockSize;
2584
2585  if (!f->length)
2586    {
2587      *last = *first;
2588    }
2589  else
2590    {
2591      offset += f->length - 1;
2592      *last = offset / tor->blockSize;
2593    }
2594}
2595
2596void
2597tr_torGetPieceBlockRange (const tr_torrent        * tor,
2598                          const tr_piece_index_t    piece,
2599                          tr_block_index_t        * first,
2600                          tr_block_index_t        * last)
2601{
2602  uint64_t offset = tor->info.pieceSize;
2603  offset *= piece;
2604  *first = offset / tor->blockSize;
2605  offset += (tr_torPieceCountBytes (tor, piece) - 1);
2606  *last = offset / tor->blockSize;
2607}
2608
2609
2610/***
2611****
2612***/
2613
2614void
2615tr_torrentSetPieceChecked (tr_torrent * tor, tr_piece_index_t pieceIndex)
2616{
2617  assert (tr_isTorrent (tor));
2618  assert (pieceIndex < tor->info.pieceCount);
2619
2620  tor->info.pieces[pieceIndex].timeChecked = tr_time ();
2621}
2622
2623void
2624tr_torrentSetChecked (tr_torrent * tor, time_t when)
2625{
2626  tr_piece_index_t i, n;
2627
2628  assert (tr_isTorrent (tor));
2629
2630  for (i=0, n=tor->info.pieceCount; i!=n; ++i)
2631    tor->info.pieces[i].timeChecked = when;
2632}
2633
2634bool
2635tr_torrentCheckPiece (tr_torrent * tor, tr_piece_index_t pieceIndex)
2636{
2637  const bool pass = tr_ioTestPiece (tor, pieceIndex);
2638
2639  tr_deeplog_tor (tor, "[LAZY] tr_torrentCheckPiece tested piece %zu, pass==%d", (size_t)pieceIndex, (int)pass);
2640  tr_torrentSetHasPiece (tor, pieceIndex, pass);
2641  tr_torrentSetPieceChecked (tor, pieceIndex);
2642  tor->anyDate = tr_time ();
2643  tr_torrentSetDirty (tor);
2644
2645  return pass;
2646}
2647
2648time_t
2649tr_torrentGetFileMTime (const tr_torrent * tor, tr_file_index_t i)
2650{
2651  time_t mtime = 0;
2652
2653  if (!tr_fdFileGetCachedMTime (tor->session, tor->uniqueId, i, &mtime))
2654    tr_torrentFindFile2 (tor, i, NULL, NULL, &mtime);
2655
2656  return mtime;
2657}
2658
2659bool
2660tr_torrentPieceNeedsCheck (const tr_torrent * tor, tr_piece_index_t p)
2661{
2662  uint64_t unused;
2663  tr_file_index_t f;
2664  const tr_info * inf = tr_torrentInfo (tor);
2665
2666  /* if we've never checked this piece, then it needs to be checked */
2667  if (!inf->pieces[p].timeChecked)
2668    return true;
2669
2670  /* If we think we've completed one of the files in this piece,
2671   * but it's been modified since we last checked it,
2672   * then it needs to be rechecked */
2673  tr_ioFindFileLocation (tor, p, 0, &f, &unused);
2674  for (; f < inf->fileCount && pieceHasFile (p, &inf->files[f]); ++f)
2675    if (tr_cpFileIsComplete (&tor->completion, f))
2676      if (tr_torrentGetFileMTime (tor, f) > inf->pieces[p].timeChecked)
2677        return true;
2678
2679  return false;
2680}
2681
2682/***
2683****
2684***/
2685
2686static int
2687compareTrackerByTier (const void * va, const void * vb)
2688{
2689  const tr_tracker_info * a = va;
2690  const tr_tracker_info * b = vb;
2691
2692  /* sort by tier */
2693  if (a->tier != b->tier)
2694    return a->tier - b->tier;
2695
2696  /* get the effects of a stable sort by comparing the two elements' addresses */
2697  return a - b;
2698}
2699
2700bool
2701tr_torrentSetAnnounceList (tr_torrent             * tor,
2702                           const tr_tracker_info  * trackers_in,
2703                           int                      trackerCount)
2704{
2705  int i;
2706  tr_variant metainfo;
2707  bool ok = true;
2708  tr_tracker_info * trackers;
2709
2710  tr_torrentLock (tor);
2711
2712  assert (tr_isTorrent (tor));
2713
2714  /* ensure the trackers' tiers are in ascending order */
2715  trackers = tr_memdup (trackers_in, sizeof (tr_tracker_info) * trackerCount);
2716  qsort (trackers, trackerCount, sizeof (tr_tracker_info), compareTrackerByTier);
2717
2718  /* look for bad URLs */
2719  for (i=0; ok && i<trackerCount; ++i)
2720    if (!tr_urlIsValidTracker (trackers[i].announce))
2721      ok = false;
2722
2723  /* save to the .torrent file */
2724  if (ok && tr_variantFromFile (&metainfo, TR_VARIANT_FMT_BENC, tor->info.torrent, NULL))
2725    {
2726      bool hasInfo;
2727      tr_info tmpInfo;
2728
2729      /* remove the old fields */
2730      tr_variantDictRemove (&metainfo, TR_KEY_announce);
2731      tr_variantDictRemove (&metainfo, TR_KEY_announce_list);
2732
2733      /* add the new fields */
2734      if (trackerCount > 0)
2735        {
2736          tr_variantDictAddStr (&metainfo, TR_KEY_announce, trackers[0].announce);
2737        }
2738      if (trackerCount > 1)
2739        {
2740          int i;
2741          int prevTier = -1;
2742          tr_variant * tier = NULL;
2743          tr_variant * announceList = tr_variantDictAddList (&metainfo, TR_KEY_announce_list, 0);
2744
2745          for (i=0; i<trackerCount; ++i)
2746            {
2747              if (prevTier != trackers[i].tier)
2748                {
2749                  prevTier = trackers[i].tier;
2750                  tier = tr_variantListAddList (announceList, 0);
2751                }
2752
2753              tr_variantListAddStr (tier, trackers[i].announce);
2754            }
2755        }
2756
2757      /* try to parse it back again, to make sure it's good */
2758      memset (&tmpInfo, 0, sizeof (tr_info));
2759      if (tr_metainfoParse (tor->session, &metainfo, &tmpInfo,
2760                            &hasInfo, &tor->infoDictLength))
2761        {
2762          /* it's good, so keep these new trackers and free the old ones */
2763
2764          tr_info swap;
2765          swap.trackers = tor->info.trackers;
2766          swap.trackerCount = tor->info.trackerCount;
2767          tor->info.trackers = tmpInfo.trackers;
2768          tor->info.trackerCount = tmpInfo.trackerCount;
2769          tmpInfo.trackers = swap.trackers;
2770          tmpInfo.trackerCount = swap.trackerCount;
2771
2772          tr_metainfoFree (&tmpInfo);
2773          tr_variantToFile (&metainfo, TR_VARIANT_FMT_BENC, tor->info.torrent);
2774        }
2775
2776      /* cleanup */
2777      tr_variantFree (&metainfo);
2778
2779      /* if we had a tracker-related error on this torrent,
2780       * and that tracker's been removed,
2781       * then clear the error */
2782      if ((tor->error == TR_STAT_TRACKER_WARNING)
2783          || (tor->error == TR_STAT_TRACKER_ERROR))
2784        {
2785          bool clear = true;
2786
2787          for (i=0; clear && i<trackerCount; ++i)
2788            if (strcmp (trackers[i].announce, tor->errorTracker) == 0)
2789              clear = false;
2790
2791          if (clear)
2792            tr_torrentClearError (tor);
2793        }
2794
2795      /* tell the announcer to reload this torrent's tracker list */
2796      tr_announcerResetTorrent (tor->session->announcer, tor);
2797    }
2798
2799  tr_torrentUnlock (tor);
2800
2801  tr_free (trackers);
2802  return ok;
2803}
2804
2805/**
2806***
2807**/
2808
2809void
2810tr_torrentSetAddedDate (tr_torrent * tor,
2811                        time_t       t)
2812{
2813  assert (tr_isTorrent (tor));
2814
2815  tor->addedDate = t;
2816  tor->anyDate = MAX (tor->anyDate, tor->addedDate);
2817}
2818
2819void
2820tr_torrentSetActivityDate (tr_torrent * tor, time_t t)
2821{
2822  assert (tr_isTorrent (tor));
2823
2824  tor->activityDate = t;
2825  tor->anyDate = MAX (tor->anyDate, tor->activityDate);
2826}
2827
2828void
2829tr_torrentSetDoneDate (tr_torrent * tor,
2830                       time_t       t)
2831{
2832  assert (tr_isTorrent (tor));
2833
2834  tor->doneDate = t;
2835  tor->anyDate = MAX (tor->anyDate, tor->doneDate);
2836}
2837
2838/**
2839***
2840**/
2841
2842uint64_t
2843tr_torrentGetBytesLeftToAllocate (const tr_torrent * tor)
2844{
2845  tr_file_index_t i;
2846  uint64_t bytesLeft = 0;
2847
2848  assert (tr_isTorrent (tor));
2849
2850  for (i=0; i<tor->info.fileCount; ++i)
2851    {
2852      if (!tor->info.files[i].dnd)
2853        {
2854          tr_sys_path_info info;
2855          const uint64_t length = tor->info.files[i].length;
2856          char * path = tr_torrentFindFile (tor, i);
2857
2858          bytesLeft += length;
2859
2860          if (path != NULL &&
2861              tr_sys_path_get_info (path, 0, &info, NULL) &&
2862              info.type == TR_SYS_PATH_IS_FILE &&
2863              info.size <= length)
2864            bytesLeft -= info.size;
2865
2866          tr_free (path);
2867        }
2868    }
2869
2870  return bytesLeft;
2871}
2872
2873/****
2874*****  Removing the torrent's local data
2875****/
2876
2877static bool
2878isJunkFile (const char * base)
2879{
2880  int i;
2881  static const char * files[] = { ".DS_Store", "desktop.ini", "Thumbs.db" };
2882  static const int file_count = sizeof (files) / sizeof (files[0]);
2883
2884  for (i=0; i<file_count; ++i)
2885    if (strcmp (base, files[i]) == 0)
2886      return true;
2887
2888#ifdef __APPLE__
2889  /* check for resource forks. <http://support.apple.com/kb/TA20578> */
2890  if (memcmp (base, "._", 2) == 0)
2891    return true;
2892#endif
2893
2894  return false;
2895}
2896
2897static void
2898removeEmptyFoldersAndJunkFiles (const char * folder)
2899{
2900  tr_sys_dir_t odir;
2901
2902  if ((odir = tr_sys_dir_open (folder, NULL)) != TR_BAD_SYS_DIR)
2903    {
2904      const char * name;
2905      while ((name = tr_sys_dir_read_name (odir, NULL)) != NULL)
2906        {
2907          if (strcmp (name, ".") != 0 && strcmp (name, "..") != 0)
2908            {
2909              tr_sys_path_info info;
2910              char * filename = tr_buildPath (folder, name, NULL);
2911
2912              if (tr_sys_path_get_info (filename, 0, &info, NULL) &&
2913                  info.type == TR_SYS_PATH_IS_DIRECTORY)
2914                removeEmptyFoldersAndJunkFiles (filename);
2915              else if (isJunkFile (name))
2916                tr_sys_path_remove (filename, NULL);
2917
2918              tr_free (filename);
2919            }
2920        }
2921
2922      tr_sys_path_remove (folder, NULL);
2923      tr_sys_dir_close (odir, NULL);
2924    }
2925}
2926
2927/**
2928 * This convoluted code does something (seemingly) simple:
2929 * remove the torrent's local files.
2930 *
2931 * Fun complications:
2932 * 1. Try to preserve the directory hierarchy in the recycle bin.
2933 * 2. If there are nontorrent files, don't delete them...
2934 * 3. ...unless the other files are "junk", such as .DS_Store
2935 */
2936static void
2937deleteLocalData (tr_torrent * tor, tr_fileFunc func)
2938{
2939  int i, n;
2940  tr_file_index_t f;
2941  char * base;
2942  tr_sys_dir_t odir;
2943  char * tmpdir = NULL;
2944  tr_ptrArray files = TR_PTR_ARRAY_INIT;
2945  tr_ptrArray folders = TR_PTR_ARRAY_INIT;
2946  PtrArrayCompareFunc vstrcmp = (PtrArrayCompareFunc)strcmp;
2947  const char * const top = tor->currentDir;
2948
2949  /* don't try to delete local data if the directory's gone missing */
2950  if (!tr_sys_path_exists (top, NULL))
2951    return;
2952
2953  /* if it's a magnet link, there's nothing to move... */
2954  if (!tr_torrentHasMetadata (tor))
2955    return;
2956
2957  /***
2958  ****  Move the local data to a new tmpdir
2959  ***/
2960
2961  base = tr_strdup_printf ("%s__XXXXXX", tr_torrentName (tor));
2962  tmpdir = tr_buildPath (top, base, NULL);
2963  tr_sys_dir_create_temp (tmpdir, NULL);
2964  tr_free (base);
2965
2966  for (f=0; f<tor->info.fileCount; ++f)
2967    {
2968      char * filename;
2969
2970      /* try to find the file, looking in the partial and download dirs */
2971      filename = tr_buildPath (top, tor->info.files[f].name, NULL);
2972      if (!tr_sys_path_exists (filename, NULL))
2973        {
2974          char * partial = tr_torrentBuildPartial (tor, f);
2975          tr_free (filename);
2976          filename = tr_buildPath (top, partial, NULL);
2977          tr_free (partial);
2978          if (!tr_sys_path_exists (filename, NULL))
2979            {
2980              tr_free (filename);
2981              filename = NULL;
2982            }
2983        }
2984
2985      /* if we found the file, move it */
2986      if (filename != NULL)
2987        {
2988          char * target = tr_buildPath (tmpdir, tor->info.files[f].name, NULL);
2989          tr_moveFile (filename, target, NULL);
2990          tr_ptrArrayAppend (&files, target);
2991          tr_free (filename);
2992        }
2993    }
2994
2995  /***
2996  ****  Remove tmpdir.
2997  ****
2998  ****  Try deleting the top-level files & folders to preserve
2999  ****  the directory hierarchy in the recycle bin.
3000  ****  If case that fails -- for example, rmdir () doesn't
3001  ****  delete nonempty folders -- go from the bottom up too.
3002  ***/
3003
3004  /* try deleting the local data's top-level files & folders */
3005  if ((odir = tr_sys_dir_open (tmpdir, NULL)) != TR_BAD_SYS_DIR)
3006    {
3007      const char * name;
3008      while ((name = tr_sys_dir_read_name (odir, NULL)) != NULL)
3009        {
3010          if (strcmp (name, ".") != 0 && strcmp (name, "..") != 0)
3011            {
3012              char * file = tr_buildPath (tmpdir, name, NULL);
3013              func (file, NULL);
3014              tr_free (file);
3015            }
3016        }
3017      tr_sys_dir_close (odir, NULL);
3018    }
3019
3020  /* go from the bottom up */
3021  for (i=0, n=tr_ptrArraySize (&files); i<n; ++i)
3022    {
3023      char * walk = tr_strdup (tr_ptrArrayNth (&files, i));
3024      while (tr_sys_path_exists (walk, NULL) && !tr_sys_path_is_same (tmpdir, walk, NULL))
3025        {
3026          char * tmp = tr_sys_path_dirname (walk, NULL);
3027          func (walk, NULL);
3028          tr_free (walk);
3029          walk = tmp;
3030        }
3031      tr_free (walk);
3032    }
3033
3034  /***
3035  ****  The local data has been removed.
3036  ****  What's left in top are empty folders, junk, and user-generated files.
3037  ****  Remove the first two categories and leave the third.
3038  ***/
3039
3040  /* build a list of 'top's child directories that belong to this torrent */
3041  for (f=0; f<tor->info.fileCount; ++f)
3042    {
3043      char * dir;
3044      char * filename;
3045
3046      /* get the directory that this file goes in... */
3047      filename = tr_buildPath (top, tor->info.files[f].name, NULL);
3048      dir = tr_sys_path_dirname (filename, NULL);
3049      tr_free (filename);
3050
3051      if (dir == NULL)
3052        continue;
3053
3054      /* walk up the directory tree until we reach 'top' */
3055      if (!tr_sys_path_is_same (top, dir, NULL) && strcmp (top, dir) != 0)
3056        {
3057          for (;;)
3058            {
3059              char * parent = tr_sys_path_dirname (dir, NULL);
3060              if (tr_sys_path_is_same (top, parent, NULL) || strcmp (top, parent) == 0)
3061                {
3062                  if (tr_ptrArrayFindSorted (&folders, dir, vstrcmp) == NULL)
3063                    tr_ptrArrayInsertSorted (&folders, tr_strdup(dir), vstrcmp);
3064                  tr_free (parent);
3065                  break;
3066                }
3067
3068              /* walk upwards to parent */
3069              tr_free (dir);
3070              dir = parent;
3071            }
3072        }
3073
3074      tr_free (dir);
3075    }
3076
3077  for (i=0, n=tr_ptrArraySize (&folders); i<n; ++i)
3078    removeEmptyFoldersAndJunkFiles (tr_ptrArrayNth (&folders, i));
3079
3080  /* cleanup */
3081  tr_sys_path_remove (tmpdir, NULL);
3082  tr_free (tmpdir);
3083  tr_ptrArrayDestruct (&folders, tr_free);
3084  tr_ptrArrayDestruct (&files, tr_free);
3085}
3086
3087static void
3088tr_torrentDeleteLocalData (tr_torrent * tor, tr_fileFunc func)
3089{
3090  assert (tr_isTorrent (tor));
3091
3092  if (func == NULL)
3093    func = tr_sys_path_remove;
3094
3095  /* close all the files because we're about to delete them */
3096  tr_cacheFlushTorrent (tor->session->cache, tor);
3097  tr_fdTorrentClose (tor->session, tor->uniqueId);
3098
3099  deleteLocalData (tor, func);
3100}
3101
3102/***
3103****
3104***/
3105
3106struct LocationData
3107{
3108  bool move_from_old_location;
3109  volatile int * setme_state;
3110  volatile double * setme_progress;
3111  char * location;
3112  tr_torrent * tor;
3113};
3114
3115static void
3116setLocation (void * vdata)
3117{
3118  bool err = false;
3119  struct LocationData * data = vdata;
3120  tr_torrent * tor = data->tor;
3121  const bool do_move = data->move_from_old_location;
3122  const char * location = data->location;
3123  double bytesHandled = 0;
3124  tr_torrentLock (tor);
3125
3126  assert (tr_isTorrent (tor));
3127
3128  tr_logAddDebug ("Moving \"%s\" location from currentDir \"%s\" to \"%s\"",
3129                  tr_torrentName (tor), tor->currentDir, location);
3130
3131  tr_sys_dir_create (location, TR_SYS_DIR_CREATE_PARENTS, 0777, NULL);
3132
3133  if (!tr_sys_path_is_same (location, tor->currentDir, NULL))
3134    {
3135      tr_file_index_t i;
3136
3137      /* bad idea to move files while they're being verified... */
3138      tr_verifyRemove (tor);
3139
3140      /* try to move the files.
3141       * FIXME: there are still all kinds of nasty cases, like what
3142       * if the target directory runs out of space halfway through... */
3143      for (i=0; !err && i<tor->info.fileCount; ++i)
3144        {
3145          char * sub;
3146          const char * oldbase;
3147          const tr_file * f = &tor->info.files[i];
3148
3149          if (tr_torrentFindFile2 (tor, i, &oldbase, &sub, NULL))
3150            {
3151              char * oldpath = tr_buildPath (oldbase, sub, NULL);
3152              char * newpath = tr_buildPath (location, sub, NULL);
3153
3154              tr_logAddDebug ("Found file #%d: %s", (int)i, oldpath);
3155
3156              if (do_move && !tr_sys_path_is_same (oldpath, newpath, NULL))
3157                {
3158                  tr_error * error = NULL;
3159
3160                  tr_logAddTorInfo (tor, "moving \"%s\" to \"%s\"", oldpath, newpath);
3161                  if (!tr_moveFile (oldpath, newpath, &error))
3162                    {
3163                      err = true;
3164                      tr_logAddTorErr (tor, "error moving \"%s\" to \"%s\": %s",
3165                                       oldpath, newpath, error->message);
3166                      tr_error_free (error);
3167                    }
3168                }
3169
3170              tr_free (newpath);
3171              tr_free (oldpath);
3172              tr_free (sub);
3173            }
3174
3175          if (data->setme_progress != NULL)
3176            {
3177              bytesHandled += f->length;
3178              *data->setme_progress = bytesHandled / tor->info.totalSize;
3179            }
3180        }
3181
3182      if (!err)
3183        {
3184          /* blow away the leftover subdirectories in the old location */
3185          if (do_move)
3186            tr_torrentDeleteLocalData (tor, tr_sys_path_remove);
3187
3188          /* set the new location and reverify */
3189          tr_torrentSetDownloadDir (tor, location);
3190        }
3191    }
3192
3193  if (!err && do_move)
3194    {
3195      tr_free (tor->incompleteDir);
3196      tor->incompleteDir = NULL;
3197      tor->currentDir = tor->downloadDir;
3198    }
3199
3200  if (data->setme_state != NULL)
3201    *data->setme_state = err ? TR_LOC_ERROR : TR_LOC_DONE;
3202
3203  /* cleanup */
3204  tr_torrentUnlock (tor);
3205  tr_free (data->location);
3206  tr_free (data);
3207}
3208
3209void
3210tr_torrentSetLocation (tr_torrent       * tor,
3211                       const char       * location,
3212                       bool               move_from_old_location,
3213                       volatile double  * setme_progress,
3214                       volatile int     * setme_state)
3215{
3216  struct LocationData * data;
3217
3218  assert (tr_isTorrent (tor));
3219
3220  if (setme_state != NULL)
3221    *setme_state = TR_LOC_MOVING;
3222
3223  if (setme_progress != NULL)
3224    *setme_progress = 0;
3225
3226  /* run this in the libtransmission thread */
3227  data = tr_new (struct LocationData, 1);
3228  data->tor = tor;
3229  data->location = tr_strdup (location);
3230  data->move_from_old_location = move_from_old_location;
3231  data->setme_state = setme_state;
3232  data->setme_progress = setme_progress;
3233  tr_runInEventThread (tor->session, setLocation, data);
3234}
3235
3236/***
3237****
3238***/
3239
3240static void
3241tr_torrentFileCompleted (tr_torrent * tor, tr_file_index_t fileIndex)
3242{
3243  char * sub;
3244  const char * base;
3245  const tr_info * inf = &tor->info;
3246  const tr_file * f = &inf->files[fileIndex];
3247  tr_piece * p;
3248  const tr_piece * pend;
3249  const time_t now = tr_time ();
3250
3251  /* close the file so that we can reopen in read-only mode as needed */
3252  tr_cacheFlushFile (tor->session->cache, tor, fileIndex);
3253  tr_fdFileClose (tor->session, tor, fileIndex);
3254
3255  /* now that the file is complete and closed, we can start watching its
3256   * mtime timestamp for changes to know if we need to reverify pieces */
3257  for (p=&inf->pieces[f->firstPiece], pend=&inf->pieces[f->lastPiece]; p!=pend; ++p)
3258    p->timeChecked = now;
3259
3260  /* if the torrent's current filename isn't the same as the one in the
3261   * metadata -- for example, if it had the ".part" suffix appended to
3262   * it until now -- then rename it to match the one in the metadata */
3263  if (tr_torrentFindFile2 (tor, fileIndex, &base, &sub, NULL))
3264    {
3265      if (strcmp (sub, f->name) != 0)
3266        {
3267          char * oldpath = tr_buildPath (base, sub, NULL);
3268          char * newpath = tr_buildPath (base, f->name, NULL);
3269          tr_error * error = NULL;
3270
3271          if (!tr_sys_path_rename (oldpath, newpath, &error))
3272            {
3273              tr_logAddTorErr (tor, "Error moving \"%s\" to \"%s\": %s", oldpath, newpath, error->message);
3274              tr_error_free (error);
3275            }
3276
3277          tr_free (newpath);
3278          tr_free (oldpath);
3279        }
3280
3281      tr_free (sub);
3282    }
3283}
3284
3285static void
3286tr_torrentPieceCompleted (tr_torrent * tor, tr_piece_index_t pieceIndex)
3287{
3288  tr_file_index_t i;
3289
3290  tr_peerMgrPieceCompleted (tor, pieceIndex);
3291
3292  /* if this piece completes any file, invoke the fileCompleted func for it */
3293  for (i=0; i<tor->info.fileCount; ++i)
3294    {
3295      const tr_file * file = &tor->info.files[i];
3296
3297      if ((file->firstPiece <= pieceIndex) && (pieceIndex <= file->lastPiece))
3298        if (tr_cpFileIsComplete (&tor->completion, i))
3299          tr_torrentFileCompleted (tor, i);
3300    }
3301}
3302
3303void
3304tr_torrentGotBlock (tr_torrent * tor, tr_block_index_t block)
3305{
3306  const bool block_is_new = !tr_torrentBlockIsComplete (tor, block);
3307
3308  assert (tr_isTorrent (tor));
3309  assert (tr_amInEventThread (tor->session));
3310
3311  if (block_is_new)
3312    {
3313      tr_piece_index_t p;
3314
3315      tr_cpBlockAdd (&tor->completion, block);
3316      tr_torrentSetDirty (tor);
3317
3318      p = tr_torBlockPiece (tor, block);
3319      if (tr_torrentPieceIsComplete (tor, p))
3320        {
3321          tr_logAddTorDbg (tor, "[LAZY] checking just-completed piece %zu", (size_t)p);
3322
3323          if (tr_torrentCheckPiece (tor, p))
3324            {
3325              tr_torrentPieceCompleted (tor, p);
3326            }
3327          else
3328            {
3329              const uint32_t n = tr_torPieceCountBytes (tor, p);
3330              tr_logAddTorErr (tor, _("Piece %"PRIu32", which was just downloaded, failed its checksum test"), p);
3331              tor->corruptCur += n;
3332              tor->downloadedCur -= MIN (tor->downloadedCur, n);
3333              tr_peerMgrGotBadPiece (tor, p);
3334            }
3335        }
3336    }
3337  else
3338    {
3339      const uint32_t n = tr_torBlockCountBytes (tor, block);
3340      tor->downloadedCur -= MIN (tor->downloadedCur, n);
3341      tr_logAddTorDbg (tor, "we have this block already...");
3342    }
3343}
3344
3345/***
3346****
3347***/
3348
3349bool
3350tr_torrentFindFile2 (const tr_torrent * tor, tr_file_index_t fileNum,
3351                     const char ** base, char ** subpath, time_t * mtime)
3352{
3353  char * part = NULL;
3354  const tr_file * file;
3355  const char * b = NULL;
3356  const char * s = NULL;
3357  tr_sys_path_info file_info;
3358
3359  assert (tr_isTorrent (tor));
3360  assert (fileNum < tor->info.fileCount);
3361
3362  file = &tor->info.files[fileNum];
3363
3364  /* look in the download dir... */
3365  if (b == NULL)
3366    {
3367      char * filename = tr_buildPath (tor->downloadDir, file->name, NULL);
3368      if (tr_sys_path_get_info (filename, 0, &file_info, NULL))
3369        {
3370          b = tor->downloadDir;
3371          s = file->name;
3372        }
3373      tr_free (filename);
3374    }
3375
3376  /* look in the incomplete dir... */
3377  if ((b == NULL) && (tor->incompleteDir != NULL))
3378    {
3379      char * filename = tr_buildPath (tor->incompleteDir, file->name, NULL);
3380      if (tr_sys_path_get_info (filename, 0, &file_info, NULL))
3381        {
3382          b = tor->incompleteDir;
3383          s = file->name;
3384        }
3385      tr_free (filename);
3386    }
3387
3388  if (b == NULL)
3389    part = tr_torrentBuildPartial (tor, fileNum);
3390
3391  /* look for a .part file in the incomplete dir... */
3392  if ((b == NULL) && (tor->incompleteDir != NULL))
3393    {
3394      char * filename = tr_buildPath (tor->incompleteDir, part, NULL);
3395      if (tr_sys_path_get_info (filename, 0, &file_info, NULL))
3396        {
3397          b = tor->incompleteDir;
3398          s = part;
3399        }
3400      tr_free (filename);
3401    }
3402
3403  /* look for a .part file in the download dir... */
3404  if (b == NULL)
3405    {
3406      char * filename = tr_buildPath (tor->downloadDir, part, NULL);
3407      if (tr_sys_path_get_info (filename, 0, &file_info, NULL))
3408        {
3409          b = tor->downloadDir;
3410          s = part;
3411        }
3412      tr_free (filename);
3413    }
3414
3415  /* return the results */
3416  if (base != NULL)
3417    *base = b;
3418  if (subpath != NULL)
3419    *subpath = tr_strdup (s);
3420  if (mtime != NULL)
3421    *mtime = file_info.last_modified_at;
3422
3423
3424  /* cleanup */
3425  tr_free (part);
3426  return b != NULL;
3427}
3428
3429char*
3430tr_torrentFindFile (const tr_torrent * tor, tr_file_index_t fileNum)
3431{
3432  char * subpath;
3433  char * ret = NULL;
3434  const char * base;
3435
3436  if (tr_torrentFindFile2 (tor, fileNum, &base, &subpath, NULL))
3437    {
3438      ret = tr_buildPath (base, subpath, NULL);
3439      tr_free (subpath);
3440    }
3441
3442    return ret;
3443}
3444
3445/* Decide whether we should be looking for files in downloadDir or incompleteDir. */
3446static void
3447refreshCurrentDir (tr_torrent * tor)
3448{
3449  const char * dir = NULL;
3450
3451  if (tor->incompleteDir == NULL)
3452    dir = tor->downloadDir;
3453  else if (!tr_torrentHasMetadata (tor)) /* no files to find */
3454    dir = tor->incompleteDir;
3455  else if (!tr_torrentFindFile2 (tor, 0, &dir, NULL, NULL))
3456    dir = tor->incompleteDir;
3457
3458  assert (dir != NULL);
3459  assert ((dir == tor->downloadDir) || (dir == tor->incompleteDir));
3460  tor->currentDir = dir;
3461}
3462
3463char*
3464tr_torrentBuildPartial (const tr_torrent * tor, tr_file_index_t fileNum)
3465{
3466  return tr_strdup_printf ("%s.part", tor->info.files[fileNum].name);
3467}
3468
3469/***
3470****
3471***/
3472
3473static int
3474compareTorrentByQueuePosition (const void * va, const void * vb)
3475{
3476  const tr_torrent * a = * (const tr_torrent * const *) va;
3477  const tr_torrent * b = * (const tr_torrent * const *) vb;
3478
3479  return a->queuePosition - b->queuePosition;
3480}
3481
3482#ifndef NDEBUG
3483static bool
3484queueIsSequenced (tr_session * session)
3485{
3486  int i;
3487  int n;
3488  bool is_sequenced;
3489  tr_torrent ** torrents;
3490
3491  n = 0;
3492  torrents = tr_sessionGetTorrents (session, &n);
3493  qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3494
3495#if 0
3496  fprintf (stderr, "%s", "queue: ");
3497  for (i=0; i<n; ++i)
3498    fprintf (stderr, "%d ", tmp[i]->queuePosition);
3499  fputc ('\n', stderr);
3500#endif
3501
3502  /* test them */
3503  is_sequenced = true;
3504  for (i=0; is_sequenced && i<n; ++i)
3505    if (torrents[i]->queuePosition != i)
3506      is_sequenced = false;
3507
3508  tr_free (torrents);
3509  return is_sequenced;
3510}
3511#endif
3512
3513int
3514tr_torrentGetQueuePosition (const tr_torrent * tor)
3515{
3516  return tor->queuePosition;
3517}
3518
3519void
3520tr_torrentSetQueuePosition (tr_torrent * tor, int pos)
3521{
3522  int back = -1;
3523  tr_torrent * walk;
3524  const int old_pos = tor->queuePosition;
3525  const time_t now = tr_time ();
3526
3527  if (pos < 0)
3528    pos = 0;
3529
3530  tor->queuePosition = -1;
3531
3532  walk = NULL;
3533  while ((walk = tr_torrentNext (tor->session, walk)))
3534    {
3535      if (old_pos < pos)
3536        {
3537          if ((old_pos <= walk->queuePosition) && (walk->queuePosition <= pos))
3538            {
3539              walk->queuePosition--;
3540              walk->anyDate = now;
3541            }
3542        }
3543
3544      if (old_pos > pos)
3545        {
3546          if ((pos <= walk->queuePosition) && (walk->queuePosition < old_pos))
3547            {
3548              walk->queuePosition++;
3549              walk->anyDate = now;
3550            }
3551        }
3552
3553      if (back < walk->queuePosition)
3554        {
3555          back = walk->queuePosition;
3556        }
3557    }
3558
3559  tor->queuePosition = MIN (pos, (back+1));
3560  tor->anyDate = now;
3561
3562  assert (queueIsSequenced (tor->session));
3563}
3564
3565void
3566tr_torrentsQueueMoveTop (tr_torrent ** torrents_in, int n)
3567{
3568  int i;
3569  tr_torrent ** torrents = tr_memdup (torrents_in, sizeof (tr_torrent *) * n);
3570  qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3571  for (i=n-1; i>=0; --i)
3572    tr_torrentSetQueuePosition (torrents[i], 0);
3573  tr_free (torrents);
3574}
3575
3576void
3577tr_torrentsQueueMoveUp (tr_torrent ** torrents_in, int n)
3578{
3579  int i;
3580  tr_torrent ** torrents;
3581
3582  torrents = tr_memdup (torrents_in, sizeof (tr_torrent *) * n);
3583  qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3584  for (i=0; i<n; ++i)
3585    tr_torrentSetQueuePosition (torrents[i], torrents[i]->queuePosition - 1);
3586
3587  tr_free (torrents);
3588}
3589
3590void
3591tr_torrentsQueueMoveDown (tr_torrent ** torrents_in, int n)
3592{
3593  int i;
3594  tr_torrent ** torrents;
3595
3596  torrents = tr_memdup (torrents_in, sizeof (tr_torrent *) * n);
3597  qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3598  for (i=n-1; i>=0; --i)
3599    tr_torrentSetQueuePosition (torrents[i], torrents[i]->queuePosition + 1);
3600
3601  tr_free (torrents);
3602}
3603
3604void
3605tr_torrentsQueueMoveBottom (tr_torrent ** torrents_in, int n)
3606{
3607  int i;
3608  tr_torrent ** torrents;
3609
3610  torrents = tr_memdup (torrents_in, sizeof (tr_torrent *) * n);
3611  qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3612  for (i=0; i<n; ++i)
3613    tr_torrentSetQueuePosition (torrents[i], INT_MAX);
3614
3615  tr_free (torrents);
3616}
3617
3618static void
3619torrentSetQueued (tr_torrent * tor, bool queued)
3620{
3621  assert (tr_isTorrent (tor));
3622  assert (tr_isBool (queued));
3623
3624  if (tr_torrentIsQueued (tor) != queued)
3625    {
3626      tor->isQueued = queued;
3627      tor->anyDate = tr_time ();
3628      tr_torrentSetDirty (tor);
3629    }
3630}
3631
3632void
3633tr_torrentSetQueueStartCallback (tr_torrent * torrent, void (*callback)(tr_torrent *, void *), void * user_data)
3634{
3635  torrent->queue_started_callback = callback;
3636  torrent->queue_started_user_data = user_data;
3637}
3638
3639
3640/***
3641****
3642****  RENAME
3643****
3644***/
3645
3646static bool
3647renameArgsAreValid (const char * oldpath, const char * newname)
3648{
3649  return (oldpath && *oldpath)
3650      && (newname && *newname)
3651      && (strcmp (newname, ".") != 0)
3652      && (strcmp (newname, "..") != 0)
3653      && (strchr (newname, TR_PATH_DELIMITER) == NULL);
3654}
3655
3656static tr_file_index_t *
3657renameFindAffectedFiles (tr_torrent * tor, const char * oldpath, size_t * setme_n)
3658{
3659  size_t n;
3660  size_t oldpath_len;
3661  tr_file_index_t i;
3662  tr_file_index_t * indices = tr_new0 (tr_file_index_t, tor->info.fileCount);
3663
3664  n = 0;
3665  oldpath_len = strlen (oldpath);
3666  for (i=0; i!=tor->info.fileCount; ++i)
3667    {
3668      const char * name = tor->info.files[i].name;
3669      const size_t len = strlen (name);
3670      if ((len == oldpath_len || (len > oldpath_len && name[oldpath_len] == '/')) &&
3671          memcmp (oldpath, name, oldpath_len) == 0)
3672        indices[n++] = i;
3673    }
3674
3675  *setme_n = n;
3676  return indices;
3677}
3678
3679static int
3680renamePath (tr_torrent  * tor,
3681            const char  * oldpath,
3682            const char  * newname)
3683{
3684  char * src;
3685  const char * base;
3686  int err = 0;
3687
3688  if (!tr_torrentIsSeed(tor) && (tor->incompleteDir != NULL))
3689    base = tor->incompleteDir;
3690  else
3691    base = tor->downloadDir;
3692
3693  src = tr_buildPath (base, oldpath, NULL);
3694  if (!tr_sys_path_exists (src, NULL)) /* check for it as a partial */
3695    {
3696      char * tmp = tr_strdup_printf ("%s.part", src);
3697      tr_free (src);
3698      src = tmp;
3699    }
3700
3701  if (tr_sys_path_exists (src, NULL))
3702    {
3703      int tmp;
3704      bool tgt_exists;
3705      char * parent = tr_sys_path_dirname (src, NULL);
3706      char * tgt;
3707
3708      if (tr_str_has_suffix (src, ".part"))
3709        tgt = tr_strdup_printf ("%s" TR_PATH_DELIMITER_STR "%s.part", parent, newname);
3710      else
3711        tgt = tr_buildPath (parent, newname, NULL);
3712
3713      tmp = errno;
3714      tgt_exists = tr_sys_path_exists (tgt, NULL);
3715      errno = tmp;
3716
3717      if (!tgt_exists)
3718        {
3719          tr_error * error = NULL;
3720
3721          tmp = errno;
3722          if (!tr_sys_path_rename (src, tgt, &error))
3723            {
3724              err = error->code;
3725              tr_error_free (error);
3726            }
3727          errno = tmp;
3728        }
3729
3730      tr_free (tgt);
3731      tr_free (parent);
3732    }
3733
3734  tr_free (src);
3735
3736  return err;
3737}
3738
3739static void
3740renameTorrentFileString (tr_torrent       * tor,
3741                         const char       * oldpath,
3742                         const char       * newname,
3743                         tr_file_index_t    fileIndex)
3744{
3745  char * name;
3746  tr_file * file = &tor->info.files[fileIndex];
3747  const size_t oldpath_len = strlen (oldpath);
3748
3749  if (strchr (oldpath, TR_PATH_DELIMITER) == NULL)
3750    {
3751      if (oldpath_len >= strlen(file->name))
3752        name = tr_buildPath (newname, NULL);
3753      else
3754        name = tr_buildPath (newname, file->name + oldpath_len + 1, NULL);
3755    }
3756  else
3757    {
3758      char * tmp = tr_sys_path_dirname (oldpath, NULL);
3759
3760      if (tmp == NULL)
3761        return;
3762
3763      if (oldpath_len >= strlen(file->name))
3764        name = tr_buildPath (tmp, newname, NULL);
3765      else
3766        name = tr_buildPath (tmp, newname, file->name + oldpath_len + 1, NULL);
3767
3768      tr_free (tmp);
3769    }
3770
3771  if (strcmp (file->name, name) == 0)
3772    {
3773      tr_free (name);
3774    }
3775  else
3776    {
3777      tr_free (file->name);
3778      file->name = name;
3779      file->is_renamed = true;
3780    }
3781}
3782
3783struct rename_data
3784{
3785  tr_torrent * tor;
3786  char * oldpath;
3787  char * newname;
3788  tr_torrent_rename_done_func callback;
3789  void * callback_user_data;
3790};
3791
3792static void
3793torrentRenamePath (void * vdata)
3794{
3795  int error = 0;
3796  struct rename_data * data = vdata;
3797  tr_torrent * const tor = data->tor;
3798  const char * const oldpath = data->oldpath;
3799  const char * const newname = data->newname;
3800
3801  /***
3802  ****
3803  ***/
3804
3805  assert (tr_isTorrent (tor));
3806
3807  if (!renameArgsAreValid (oldpath, newname))
3808    {
3809      error = EINVAL;
3810    }
3811  else
3812    {
3813      size_t n;
3814      tr_file_index_t * file_indices;
3815
3816      file_indices = renameFindAffectedFiles (tor, oldpath, &n);
3817      if (n == 0)
3818        {
3819          error = EINVAL;
3820        }
3821      else
3822        {
3823          size_t i;
3824
3825          error = renamePath (tor, oldpath, newname);
3826
3827          if (!error)
3828            {
3829              /* update tr_info.files */
3830              for (i=0; i<n; ++i)
3831                renameTorrentFileString(tor, oldpath, newname, file_indices[i]);
3832
3833              /* update tr_info.name if user changed the toplevel */
3834              if ((n == tor->info.fileCount) && (strchr(oldpath,'/')==NULL))
3835                {
3836                  tr_free (tor->info.name);
3837                  tor->info.name = tr_strdup (newname);
3838                }
3839
3840              tr_torrentSetDirty (tor);
3841            }
3842        }
3843
3844      tr_free (file_indices);
3845    }
3846
3847
3848  /***
3849  ****
3850  ***/
3851
3852  tor->anyDate = tr_time ();
3853
3854  /* callback */
3855  if (data->callback != NULL)
3856    (*data->callback)(tor, data->oldpath, data->newname, error, data->callback_user_data);
3857
3858  /* cleanup */
3859  tr_free (data->oldpath);
3860  tr_free (data->newname);
3861  tr_free (data);
3862}
3863
3864void
3865tr_torrentRenamePath (tr_torrent                  * tor,
3866                      const char                  * oldpath,
3867                      const char                  * newname,
3868                      tr_torrent_rename_done_func   callback,
3869                      void                        * callback_user_data)
3870{
3871  struct rename_data * data;
3872
3873  data = tr_new0 (struct rename_data, 1);
3874  data->tor = tor;
3875  data->oldpath = tr_strdup (oldpath);
3876  data->newname = tr_strdup (newname);
3877  data->callback = callback;
3878  data->callback_user_data = callback_user_data;
3879
3880  tr_runInEventThread (tor->session, torrentRenamePath, data);
3881}
Note: See TracBrowser for help on using the repository browser.