source: trunk/libtransmission/torrent.c @ 14530

Last change on this file since 14530 was 14530, checked in by mikedld, 6 years ago

Use CreateProcess? instead of _spawnvpe to execute scripts on Windows

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