source: trunk/libtransmission/torrent.c @ 14525

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

Fix some issues revealed by coverity

  • Property svn:keywords set to Date Rev Author Id
File size: 93.2 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 14525 2015-05-09 08:37:55Z 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 <process.h> /* _spawnvpe () */
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      int i;
2109      char * cmd[] = { tr_strdup (script), NULL };
2110      char * env[] = {
2111        tr_strdup_printf ("TR_APP_VERSION=%s", SHORT_VERSION_STRING),
2112        tr_strdup_printf ("TR_TIME_LOCALTIME=%s", timeStr),
2113        tr_strdup_printf ("TR_TORRENT_DIR=%s", tor->currentDir),
2114        tr_strdup_printf ("TR_TORRENT_ID=%d", tr_torrentId (tor)),
2115        tr_strdup_printf ("TR_TORRENT_HASH=%s", tor->info.hashString),
2116        tr_strdup_printf ("TR_TORRENT_NAME=%s", tr_torrentName (tor)),
2117        NULL };
2118
2119      tr_logAddTorInfo (tor, "Calling script \"%s\"", script);
2120
2121#ifdef _WIN32
2122      if (_spawnvpe (_P_NOWAIT, script, (const char* const*)cmd, (const char* const*)env) == -1)
2123        tr_logAddTorErr (tor, "error executing script \"%s\": %s", cmd[0], tr_strerror (errno));
2124#else
2125      signal (SIGCHLD, onSigCHLD);
2126
2127      if (!fork ())
2128        {
2129          for (i=0; env[i]; ++i)
2130            putenv (env[i]);
2131
2132          if (execvp (script, cmd) == -1)
2133            tr_logAddTorErr (tor, "error executing script \"%s\": %s", cmd[0], tr_strerror (errno));
2134
2135          _exit (0);
2136        }
2137#endif
2138
2139      for (i=0; cmd[i]; ++i) tr_free (cmd[i]);
2140      for (i=0; env[i]; ++i) tr_free (env[i]);
2141    }
2142}
2143
2144void
2145tr_torrentRecheckCompleteness (tr_torrent * tor)
2146{
2147  tr_completeness completeness;
2148
2149  tr_torrentLock (tor);
2150
2151  completeness = tr_cpGetStatus (&tor->completion);
2152  if (completeness != tor->completeness)
2153    {
2154      const bool recentChange = tor->downloadedCur != 0;
2155      const bool wasLeeching = !tr_torrentIsSeed (tor);
2156      const bool wasRunning = tor->isRunning;
2157
2158      if (recentChange)
2159        tr_logAddTorInfo (tor, _("State changed from \"%1$s\" to \"%2$s\""),
2160                          getCompletionString (tor->completeness),
2161                          getCompletionString (completeness));
2162
2163      tor->completeness = completeness;
2164      tr_fdTorrentClose (tor->session, tor->uniqueId);
2165
2166      if (tr_torrentIsSeed (tor))
2167        {
2168          if (recentChange)
2169            {
2170              tr_announcerTorrentCompleted (tor);
2171              tor->doneDate = tor->anyDate = tr_time ();
2172            }
2173
2174          if (wasLeeching && wasRunning)
2175            {
2176              /* clear interested flag on all peers */
2177              tr_peerMgrClearInterest (tor);
2178            }
2179
2180          if (tor->currentDir == tor->incompleteDir)
2181            tr_torrentSetLocation (tor, tor->downloadDir, true, NULL, NULL);
2182        }
2183
2184      fireCompletenessChange (tor, completeness, wasRunning);
2185
2186      if (tr_torrentIsSeed (tor))
2187        {
2188          if (wasLeeching && wasRunning)
2189            {
2190              /* if completeness was TR_LEECH then the seed limit check will have been skipped in bandwidthPulse */
2191              tr_torrentCheckSeedLimit (tor);
2192            }
2193
2194          if (tr_sessionIsTorrentDoneScriptEnabled (tor->session))
2195            torrentCallScript (tor, tr_sessionGetTorrentDoneScript (tor->session));
2196        }
2197
2198      tr_torrentSetDirty (tor);
2199    }
2200
2201  tr_torrentUnlock (tor);
2202}
2203
2204/***
2205****
2206***/
2207
2208static void
2209tr_torrentFireMetadataCompleted (tr_torrent * tor)
2210{
2211  assert (tr_isTorrent (tor));
2212
2213  if (tor->metadata_func != NULL)
2214    tor->metadata_func (tor, tor->metadata_func_user_data);
2215}
2216
2217void
2218tr_torrentSetMetadataCallback (tr_torrent                * tor,
2219                               tr_torrent_metadata_func    func,
2220                               void                      * user_data)
2221{
2222  assert (tr_isTorrent (tor));
2223
2224  tor->metadata_func = func;
2225  tor->metadata_func_user_data = user_data;
2226}
2227
2228
2229/**
2230***  File priorities
2231**/
2232
2233void
2234tr_torrentInitFilePriority (tr_torrent *    tor,
2235                            tr_file_index_t fileIndex,
2236                            tr_priority_t   priority)
2237{
2238  tr_file * file;
2239  tr_piece_index_t i;
2240
2241  assert (tr_isTorrent (tor));
2242  assert (fileIndex < tor->info.fileCount);
2243  assert (tr_isPriority (priority));
2244
2245  file = &tor->info.files[fileIndex];
2246  file->priority = priority;
2247  for (i=file->firstPiece; i<=file->lastPiece; ++i)
2248    tor->info.pieces[i].priority = calculatePiecePriority (tor, i, fileIndex);
2249}
2250
2251void
2252tr_torrentSetFilePriorities (tr_torrent             * tor,
2253                             const tr_file_index_t  * files,
2254                             tr_file_index_t          fileCount,
2255                             tr_priority_t            priority)
2256{
2257  tr_file_index_t i;
2258  assert (tr_isTorrent (tor));
2259  tr_torrentLock (tor);
2260
2261  for (i=0; i<fileCount; ++i)
2262    if (files[i] < tor->info.fileCount)
2263      tr_torrentInitFilePriority (tor, files[i], priority);
2264  tr_torrentSetDirty (tor);
2265  tr_peerMgrRebuildRequests (tor);
2266
2267  tr_torrentUnlock (tor);
2268}
2269
2270tr_priority_t*
2271tr_torrentGetFilePriorities (const tr_torrent * tor)
2272{
2273  tr_file_index_t i;
2274  tr_priority_t * p;
2275
2276  assert (tr_isTorrent (tor));
2277
2278  p = tr_new0 (tr_priority_t, tor->info.fileCount);
2279
2280  for (i=0; i<tor->info.fileCount; ++i)
2281    p[i] = tor->info.files[i].priority;
2282
2283  return p;
2284}
2285
2286/**
2287***  File DND
2288**/
2289
2290static void
2291setFileDND (tr_torrent * tor, tr_file_index_t fileIndex, int doDownload)
2292{
2293  const int8_t dnd = !doDownload;
2294  tr_piece_index_t firstPiece;
2295  int8_t firstPieceDND;
2296  tr_piece_index_t lastPiece;
2297  int8_t lastPieceDND;
2298  tr_file_index_t  i;
2299  tr_file * file = &tor->info.files[fileIndex];
2300
2301  file->dnd = dnd;
2302  firstPiece = file->firstPiece;
2303  lastPiece = file->lastPiece;
2304
2305  /* can't set the first piece to DND unless
2306     every file using that piece is DND */
2307  firstPieceDND = dnd;
2308  if (fileIndex > 0)
2309    {
2310      for (i=fileIndex-1; firstPieceDND; --i)
2311        {
2312          if (tor->info.files[i].lastPiece != firstPiece)
2313            break;
2314
2315          firstPieceDND = tor->info.files[i].dnd;
2316          if (!i)
2317            break;
2318        }
2319    }
2320
2321  /* can't set the last piece to DND unless
2322     every file using that piece is DND */
2323  lastPieceDND = dnd;
2324  for (i=fileIndex+1; lastPieceDND && i<tor->info.fileCount; ++i)
2325    {
2326      if (tor->info.files[i].firstPiece != lastPiece)
2327        break;
2328      lastPieceDND = tor->info.files[i].dnd;
2329    }
2330
2331  if (firstPiece == lastPiece)
2332    {
2333      tor->info.pieces[firstPiece].dnd = firstPieceDND && lastPieceDND;
2334    }
2335  else
2336    {
2337      tr_piece_index_t pp;
2338      tor->info.pieces[firstPiece].dnd = firstPieceDND;
2339      tor->info.pieces[lastPiece].dnd = lastPieceDND;
2340      for (pp=firstPiece+1; pp<lastPiece; ++pp)
2341        tor->info.pieces[pp].dnd = dnd;
2342    }
2343}
2344
2345void
2346tr_torrentInitFileDLs (tr_torrent             * tor,
2347                       const tr_file_index_t  * files,
2348                       tr_file_index_t          fileCount,
2349                       bool                     doDownload)
2350{
2351  tr_file_index_t i;
2352
2353  assert (tr_isTorrent (tor));
2354
2355  tr_torrentLock (tor);
2356
2357  for (i=0; i<fileCount; ++i)
2358    if (files[i] < tor->info.fileCount)
2359      setFileDND (tor, files[i], doDownload);
2360
2361  tr_cpInvalidateDND (&tor->completion);
2362
2363  tr_torrentUnlock (tor);
2364}
2365
2366void
2367tr_torrentSetFileDLs (tr_torrent             * tor,
2368                      const tr_file_index_t  * files,
2369                      tr_file_index_t          fileCount,
2370                      bool                     doDownload)
2371{
2372  assert (tr_isTorrent (tor));
2373  tr_torrentLock (tor);
2374
2375  tr_torrentInitFileDLs (tor, files, fileCount, doDownload);
2376  tr_torrentSetDirty (tor);
2377  tr_torrentRecheckCompleteness (tor);
2378  tr_peerMgrRebuildRequests (tor);
2379
2380  tr_torrentUnlock (tor);
2381}
2382
2383/***
2384****
2385***/
2386
2387tr_priority_t
2388tr_torrentGetPriority (const tr_torrent * tor)
2389{
2390  assert (tr_isTorrent (tor));
2391
2392  return tor->bandwidth.priority;
2393}
2394
2395void
2396tr_torrentSetPriority (tr_torrent * tor, tr_priority_t priority)
2397{
2398  assert (tr_isTorrent (tor));
2399  assert (tr_isPriority (priority));
2400
2401  if (tor->bandwidth.priority != priority)
2402    {
2403      tor->bandwidth.priority = priority;
2404
2405      tr_torrentSetDirty (tor);
2406    }
2407}
2408
2409/***
2410****
2411***/
2412
2413void
2414tr_torrentSetPeerLimit (tr_torrent * tor,
2415                        uint16_t     maxConnectedPeers)
2416{
2417  assert (tr_isTorrent (tor));
2418
2419  if (tor->maxConnectedPeers != maxConnectedPeers)
2420    {
2421      tor->maxConnectedPeers = maxConnectedPeers;
2422
2423      tr_torrentSetDirty (tor);
2424    }
2425}
2426
2427uint16_t
2428tr_torrentGetPeerLimit (const tr_torrent * tor)
2429{
2430  assert (tr_isTorrent (tor));
2431
2432  return tor->maxConnectedPeers;
2433}
2434
2435/***
2436****
2437***/
2438
2439void
2440tr_torrentGetBlockLocation (const tr_torrent * tor,
2441                            tr_block_index_t   block,
2442                            tr_piece_index_t * piece,
2443                            uint32_t         * offset,
2444                            uint32_t         * length)
2445{
2446  uint64_t pos = block;
2447  pos *= tor->blockSize;
2448  *piece = pos / tor->info.pieceSize;
2449  *offset = pos - (*piece * tor->info.pieceSize);
2450  *length = tr_torBlockCountBytes (tor, block);
2451}
2452
2453
2454tr_block_index_t
2455_tr_block (const tr_torrent * tor,
2456           tr_piece_index_t   index,
2457           uint32_t           offset)
2458{
2459  tr_block_index_t ret;
2460
2461  assert (tr_isTorrent (tor));
2462
2463  ret = index;
2464  ret *= (tor->info.pieceSize / tor->blockSize);
2465  ret += offset / tor->blockSize;
2466  return ret;
2467}
2468
2469bool
2470tr_torrentReqIsValid (const tr_torrent * tor,
2471                      tr_piece_index_t   index,
2472                      uint32_t           offset,
2473                      uint32_t           length)
2474{
2475  int err = 0;
2476
2477  assert (tr_isTorrent (tor));
2478
2479  if (index >= tor->info.pieceCount)
2480    err = 1;
2481  else if (length < 1)
2482    err = 2;
2483  else if ((offset + length) > tr_torPieceCountBytes (tor, index))
2484    err = 3;
2485  else if (length > MAX_BLOCK_SIZE)
2486    err = 4;
2487  else if (tr_pieceOffset (tor, index, offset, length) > tor->info.totalSize)
2488    err = 5;
2489
2490  if (err)
2491    tr_logAddTorDbg (tor, "index %lu offset %lu length %lu err %d\n",
2492                     (unsigned long)index,
2493                     (unsigned long)offset,
2494                     (unsigned long)length,
2495                     err);
2496
2497  return !err;
2498}
2499
2500uint64_t
2501tr_pieceOffset (const tr_torrent * tor,
2502                tr_piece_index_t   index,
2503                uint32_t           offset,
2504                uint32_t           length)
2505{
2506  uint64_t ret;
2507
2508  assert (tr_isTorrent (tor));
2509
2510  ret = tor->info.pieceSize;
2511  ret *= index;
2512  ret += offset;
2513  ret += length;
2514  return ret;
2515}
2516
2517void
2518tr_torGetFileBlockRange (const tr_torrent        * tor,
2519                         const tr_file_index_t     file,
2520                         tr_block_index_t        * first,
2521                         tr_block_index_t        * last)
2522{
2523  const tr_file * f = &tor->info.files[file];
2524  uint64_t offset = f->offset;
2525
2526  *first = offset / tor->blockSize;
2527
2528  if (!f->length)
2529    {
2530      *last = *first;
2531    }
2532  else
2533    {
2534      offset += f->length - 1;
2535      *last = offset / tor->blockSize;
2536    }
2537}
2538
2539void
2540tr_torGetPieceBlockRange (const tr_torrent        * tor,
2541                          const tr_piece_index_t    piece,
2542                          tr_block_index_t        * first,
2543                          tr_block_index_t        * last)
2544{
2545  uint64_t offset = tor->info.pieceSize;
2546  offset *= piece;
2547  *first = offset / tor->blockSize;
2548  offset += (tr_torPieceCountBytes (tor, piece) - 1);
2549  *last = offset / tor->blockSize;
2550}
2551
2552
2553/***
2554****
2555***/
2556
2557void
2558tr_torrentSetPieceChecked (tr_torrent * tor, tr_piece_index_t pieceIndex)
2559{
2560  assert (tr_isTorrent (tor));
2561  assert (pieceIndex < tor->info.pieceCount);
2562
2563  tor->info.pieces[pieceIndex].timeChecked = tr_time ();
2564}
2565
2566void
2567tr_torrentSetChecked (tr_torrent * tor, time_t when)
2568{
2569  tr_piece_index_t i, n;
2570
2571  assert (tr_isTorrent (tor));
2572
2573  for (i=0, n=tor->info.pieceCount; i!=n; ++i)
2574    tor->info.pieces[i].timeChecked = when;
2575}
2576
2577bool
2578tr_torrentCheckPiece (tr_torrent * tor, tr_piece_index_t pieceIndex)
2579{
2580  const bool pass = tr_ioTestPiece (tor, pieceIndex);
2581
2582  tr_deeplog_tor (tor, "[LAZY] tr_torrentCheckPiece tested piece %"TR_PRIuSIZE", pass==%d", (size_t)pieceIndex, (int)pass);
2583  tr_torrentSetHasPiece (tor, pieceIndex, pass);
2584  tr_torrentSetPieceChecked (tor, pieceIndex);
2585  tor->anyDate = tr_time ();
2586  tr_torrentSetDirty (tor);
2587
2588  return pass;
2589}
2590
2591time_t
2592tr_torrentGetFileMTime (const tr_torrent * tor, tr_file_index_t i)
2593{
2594  time_t mtime = 0;
2595
2596  if (!tr_fdFileGetCachedMTime (tor->session, tor->uniqueId, i, &mtime))
2597    tr_torrentFindFile2 (tor, i, NULL, NULL, &mtime);
2598
2599  return mtime;
2600}
2601
2602bool
2603tr_torrentPieceNeedsCheck (const tr_torrent * tor, tr_piece_index_t p)
2604{
2605  uint64_t unused;
2606  tr_file_index_t f;
2607  const tr_info * inf = tr_torrentInfo (tor);
2608
2609  /* if we've never checked this piece, then it needs to be checked */
2610  if (!inf->pieces[p].timeChecked)
2611    return true;
2612
2613  /* If we think we've completed one of the files in this piece,
2614   * but it's been modified since we last checked it,
2615   * then it needs to be rechecked */
2616  tr_ioFindFileLocation (tor, p, 0, &f, &unused);
2617  for (; f < inf->fileCount && pieceHasFile (p, &inf->files[f]); ++f)
2618    if (tr_cpFileIsComplete (&tor->completion, f))
2619      if (tr_torrentGetFileMTime (tor, f) > inf->pieces[p].timeChecked)
2620        return true;
2621
2622  return false;
2623}
2624
2625/***
2626****
2627***/
2628
2629static int
2630compareTrackerByTier (const void * va, const void * vb)
2631{
2632  const tr_tracker_info * a = va;
2633  const tr_tracker_info * b = vb;
2634
2635  /* sort by tier */
2636  if (a->tier != b->tier)
2637    return a->tier - b->tier;
2638
2639  /* get the effects of a stable sort by comparing the two elements' addresses */
2640  return a - b;
2641}
2642
2643bool
2644tr_torrentSetAnnounceList (tr_torrent             * tor,
2645                           const tr_tracker_info  * trackers_in,
2646                           int                      trackerCount)
2647{
2648  int i;
2649  tr_variant metainfo;
2650  bool ok = true;
2651  tr_tracker_info * trackers;
2652
2653  tr_torrentLock (tor);
2654
2655  assert (tr_isTorrent (tor));
2656
2657  /* ensure the trackers' tiers are in ascending order */
2658  trackers = tr_memdup (trackers_in, sizeof (tr_tracker_info) * trackerCount);
2659  qsort (trackers, trackerCount, sizeof (tr_tracker_info), compareTrackerByTier);
2660
2661  /* look for bad URLs */
2662  for (i=0; ok && i<trackerCount; ++i)
2663    if (!tr_urlIsValidTracker (trackers[i].announce))
2664      ok = false;
2665
2666  /* save to the .torrent file */
2667  if (ok && tr_variantFromFile (&metainfo, TR_VARIANT_FMT_BENC, tor->info.torrent, NULL))
2668    {
2669      bool hasInfo;
2670      tr_info tmpInfo;
2671
2672      /* remove the old fields */
2673      tr_variantDictRemove (&metainfo, TR_KEY_announce);
2674      tr_variantDictRemove (&metainfo, TR_KEY_announce_list);
2675
2676      /* add the new fields */
2677      if (trackerCount > 0)
2678        {
2679          tr_variantDictAddStr (&metainfo, TR_KEY_announce, trackers[0].announce);
2680        }
2681      if (trackerCount > 1)
2682        {
2683          int i;
2684          int prevTier = -1;
2685          tr_variant * tier = NULL;
2686          tr_variant * announceList = tr_variantDictAddList (&metainfo, TR_KEY_announce_list, 0);
2687
2688          for (i=0; i<trackerCount; ++i)
2689            {
2690              if (prevTier != trackers[i].tier)
2691                {
2692                  prevTier = trackers[i].tier;
2693                  tier = tr_variantListAddList (announceList, 0);
2694                }
2695
2696              tr_variantListAddStr (tier, trackers[i].announce);
2697            }
2698        }
2699
2700      /* try to parse it back again, to make sure it's good */
2701      memset (&tmpInfo, 0, sizeof (tr_info));
2702      if (tr_metainfoParse (tor->session, &metainfo, &tmpInfo,
2703                            &hasInfo, &tor->infoDictLength))
2704        {
2705          /* it's good, so keep these new trackers and free the old ones */
2706
2707          tr_info swap;
2708          swap.trackers = tor->info.trackers;
2709          swap.trackerCount = tor->info.trackerCount;
2710          tor->info.trackers = tmpInfo.trackers;
2711          tor->info.trackerCount = tmpInfo.trackerCount;
2712          tmpInfo.trackers = swap.trackers;
2713          tmpInfo.trackerCount = swap.trackerCount;
2714
2715          tr_metainfoFree (&tmpInfo);
2716          tr_variantToFile (&metainfo, TR_VARIANT_FMT_BENC, tor->info.torrent);
2717        }
2718
2719      /* cleanup */
2720      tr_variantFree (&metainfo);
2721
2722      /* if we had a tracker-related error on this torrent,
2723       * and that tracker's been removed,
2724       * then clear the error */
2725      if ((tor->error == TR_STAT_TRACKER_WARNING)
2726          || (tor->error == TR_STAT_TRACKER_ERROR))
2727        {
2728          bool clear = true;
2729
2730          for (i=0; clear && i<trackerCount; ++i)
2731            if (!strcmp (trackers[i].announce, tor->errorTracker))
2732              clear = false;
2733
2734          if (clear)
2735            tr_torrentClearError (tor);
2736        }
2737
2738      /* tell the announcer to reload this torrent's tracker list */
2739      tr_announcerResetTorrent (tor->session->announcer, tor);
2740    }
2741
2742  tr_torrentUnlock (tor);
2743
2744  tr_free (trackers);
2745  return ok;
2746}
2747
2748/**
2749***
2750**/
2751
2752void
2753tr_torrentSetAddedDate (tr_torrent * tor,
2754                        time_t       t)
2755{
2756  assert (tr_isTorrent (tor));
2757
2758  tor->addedDate = t;
2759  tor->anyDate = MAX (tor->anyDate, tor->addedDate);
2760}
2761
2762void
2763tr_torrentSetActivityDate (tr_torrent * tor, time_t t)
2764{
2765  assert (tr_isTorrent (tor));
2766
2767  tor->activityDate = t;
2768  tor->anyDate = MAX (tor->anyDate, tor->activityDate);
2769}
2770
2771void
2772tr_torrentSetDoneDate (tr_torrent * tor,
2773                       time_t       t)
2774{
2775  assert (tr_isTorrent (tor));
2776
2777  tor->doneDate = t;
2778  tor->anyDate = MAX (tor->anyDate, tor->doneDate);
2779}
2780
2781/**
2782***
2783**/
2784
2785uint64_t
2786tr_torrentGetBytesLeftToAllocate (const tr_torrent * tor)
2787{
2788  tr_file_index_t i;
2789  uint64_t bytesLeft = 0;
2790
2791  assert (tr_isTorrent (tor));
2792
2793  for (i=0; i<tor->info.fileCount; ++i)
2794    {
2795      if (!tor->info.files[i].dnd)
2796        {
2797          tr_sys_path_info info;
2798          const uint64_t length = tor->info.files[i].length;
2799          char * path = tr_torrentFindFile (tor, i);
2800
2801          bytesLeft += length;
2802
2803          if (path != NULL &&
2804              tr_sys_path_get_info (path, 0, &info, NULL) &&
2805              info.type == TR_SYS_PATH_IS_FILE &&
2806              info.size <= length)
2807            bytesLeft -= info.size;
2808
2809          tr_free (path);
2810        }
2811    }
2812
2813  return bytesLeft;
2814}
2815
2816/****
2817*****  Removing the torrent's local data
2818****/
2819
2820static bool
2821isJunkFile (const char * base)
2822{
2823  int i;
2824  static const char * files[] = { ".DS_Store", "desktop.ini", "Thumbs.db" };
2825  static const int file_count = sizeof (files) / sizeof (files[0]);
2826
2827  for (i=0; i<file_count; ++i)
2828    if (!strcmp (base, files[i]))
2829      return true;
2830
2831#ifdef __APPLE__
2832  /* check for resource forks. <http://support.apple.com/kb/TA20578> */
2833  if (!memcmp (base, "._", 2))
2834    return true;
2835#endif
2836
2837  return false;
2838}
2839
2840static void
2841removeEmptyFoldersAndJunkFiles (const char * folder)
2842{
2843  tr_sys_dir_t odir;
2844
2845  if ((odir = tr_sys_dir_open (folder, NULL)) != TR_BAD_SYS_DIR)
2846    {
2847      const char * name;
2848      while ((name = tr_sys_dir_read_name (odir, NULL)) != NULL)
2849        {
2850          if (strcmp (name, ".") != 0 && strcmp (name, "..") != 0)
2851            {
2852              tr_sys_path_info info;
2853              char * filename = tr_buildPath (folder, name, NULL);
2854
2855              if (tr_sys_path_get_info (filename, 0, &info, NULL) &&
2856                  info.type == TR_SYS_PATH_IS_DIRECTORY)
2857                removeEmptyFoldersAndJunkFiles (filename);
2858              else if (isJunkFile (name))
2859                tr_sys_path_remove (filename, NULL);
2860
2861              tr_free (filename);
2862            }
2863        }
2864
2865      tr_sys_path_remove (folder, NULL);
2866      tr_sys_dir_close (odir, NULL);
2867    }
2868}
2869
2870/**
2871 * This convoluted code does something (seemingly) simple:
2872 * remove the torrent's local files.
2873 *
2874 * Fun complications:
2875 * 1. Try to preserve the directory hierarchy in the recycle bin.
2876 * 2. If there are nontorrent files, don't delete them...
2877 * 3. ...unless the other files are "junk", such as .DS_Store
2878 */
2879static void
2880deleteLocalData (tr_torrent * tor, tr_fileFunc func)
2881{
2882  int i, n;
2883  tr_file_index_t f;
2884  char * base;
2885  tr_sys_dir_t odir;
2886  char * tmpdir = NULL;
2887  tr_ptrArray files = TR_PTR_ARRAY_INIT;
2888  tr_ptrArray folders = TR_PTR_ARRAY_INIT;
2889  PtrArrayCompareFunc vstrcmp = (PtrArrayCompareFunc)strcmp;
2890  const char * const top = tor->currentDir;
2891
2892  /* don't try to delete local data if the directory's gone missing */
2893  if (!tr_sys_path_exists (top, NULL))
2894    return;
2895
2896  /* if it's a magnet link, there's nothing to move... */
2897  if (!tr_torrentHasMetadata (tor))
2898    return;
2899
2900  /***
2901  ****  Move the local data to a new tmpdir
2902  ***/
2903
2904  base = tr_strdup_printf ("%s__XXXXXX", tr_torrentName (tor));
2905  tmpdir = tr_buildPath (top, base, NULL);
2906  tr_sys_dir_create_temp (tmpdir, NULL);
2907  tr_free (base);
2908
2909  for (f=0; f<tor->info.fileCount; ++f)
2910    {
2911      char * filename;
2912
2913      /* try to find the file, looking in the partial and download dirs */
2914      filename = tr_buildPath (top, tor->info.files[f].name, NULL);
2915      if (!tr_sys_path_exists (filename, NULL))
2916        {
2917          char * partial = tr_torrentBuildPartial (tor, f);
2918          tr_free (filename);
2919          filename = tr_buildPath (top, partial, NULL);
2920          tr_free (partial);
2921          if (!tr_sys_path_exists (filename, NULL))
2922            {
2923              tr_free (filename);
2924              filename = NULL;
2925            }
2926        }
2927
2928      /* if we found the file, move it */
2929      if (filename != NULL)
2930        {
2931          char * target = tr_buildPath (tmpdir, tor->info.files[f].name, NULL);
2932          tr_moveFile (filename, target, NULL);
2933          tr_ptrArrayAppend (&files, target);
2934          tr_free (filename);
2935        }
2936    }
2937
2938  /***
2939  ****  Remove tmpdir.
2940  ****
2941  ****  Try deleting the top-level files & folders to preserve
2942  ****  the directory hierarchy in the recycle bin.
2943  ****  If case that fails -- for example, rmdir () doesn't
2944  ****  delete nonempty folders -- go from the bottom up too.
2945  ***/
2946
2947  /* try deleting the local data's top-level files & folders */
2948  if ((odir = tr_sys_dir_open (tmpdir, NULL)) != TR_BAD_SYS_DIR)
2949    {
2950      const char * name;
2951      while ((name = tr_sys_dir_read_name (odir, NULL)) != NULL)
2952        {
2953          if (strcmp (name, ".") != 0 && strcmp (name, "..") != 0)
2954            {
2955              char * file = tr_buildPath (tmpdir, name, NULL);
2956              func (file);
2957              tr_free (file);
2958            }
2959        }
2960      tr_sys_dir_close (odir, NULL);
2961    }
2962
2963  /* go from the bottom up */
2964  for (i=0, n=tr_ptrArraySize (&files); i<n; ++i)
2965    {
2966      char * walk = tr_strdup (tr_ptrArrayNth (&files, i));
2967      while (tr_sys_path_exists (walk, NULL) && !tr_sys_path_is_same (tmpdir, walk, NULL))
2968        {
2969          char * tmp = tr_sys_path_dirname (walk, NULL);
2970          func (walk);
2971          tr_free (walk);
2972          walk = tmp;
2973        }
2974      tr_free (walk);
2975    }
2976
2977  /***
2978  ****  The local data has been removed.
2979  ****  What's left in top are empty folders, junk, and user-generated files.
2980  ****  Remove the first two categories and leave the third.
2981  ***/
2982
2983  /* build a list of 'top's child directories that belong to this torrent */
2984  for (f=0; f<tor->info.fileCount; ++f)
2985    {
2986      char * dir;
2987      char * filename;
2988
2989      /* get the directory that this file goes in... */
2990      filename = tr_buildPath (top, tor->info.files[f].name, NULL);
2991      dir = tr_sys_path_dirname (filename, NULL);
2992      tr_free (filename);
2993
2994      /* walk up the directory tree until we reach 'top' */
2995      if (!tr_sys_path_is_same (top, dir, NULL) && strcmp (top, dir) != 0)
2996        {
2997          for (;;)
2998            {
2999              char * parent = tr_sys_path_dirname (dir, NULL);
3000              if (tr_sys_path_is_same (top, parent, NULL) || strcmp (top, parent) == 0)
3001                {
3002                  if (tr_ptrArrayFindSorted (&folders, dir, vstrcmp) == NULL)
3003                    tr_ptrArrayInsertSorted (&folders, tr_strdup(dir), vstrcmp);
3004                  tr_free (parent);
3005                  break;
3006                }
3007
3008              /* walk upwards to parent */
3009              tr_free (dir);
3010              dir = parent;
3011            }
3012        }
3013
3014      tr_free (dir);
3015    }
3016
3017  for (i=0, n=tr_ptrArraySize (&folders); i<n; ++i)
3018    removeEmptyFoldersAndJunkFiles (tr_ptrArrayNth (&folders, i));
3019
3020  /* cleanup */
3021  tr_sys_path_remove (tmpdir, NULL);
3022  tr_free (tmpdir);
3023  tr_ptrArrayDestruct (&folders, tr_free);
3024  tr_ptrArrayDestruct (&files, tr_free);
3025}
3026
3027static void
3028tr_torrentDeleteLocalData (tr_torrent * tor, tr_fileFunc func)
3029{
3030  assert (tr_isTorrent (tor));
3031
3032  if (func == NULL)
3033    func = remove;
3034
3035  /* close all the files because we're about to delete them */
3036  tr_cacheFlushTorrent (tor->session->cache, tor);
3037  tr_fdTorrentClose (tor->session, tor->uniqueId);
3038
3039  deleteLocalData (tor, func);
3040}
3041
3042/***
3043****
3044***/
3045
3046struct LocationData
3047{
3048  bool move_from_old_location;
3049  volatile int * setme_state;
3050  volatile double * setme_progress;
3051  char * location;
3052  tr_torrent * tor;
3053};
3054
3055static void
3056setLocation (void * vdata)
3057{
3058  bool err = false;
3059  struct LocationData * data = vdata;
3060  tr_torrent * tor = data->tor;
3061  const bool do_move = data->move_from_old_location;
3062  const char * location = data->location;
3063  double bytesHandled = 0;
3064  tr_torrentLock (tor);
3065
3066  assert (tr_isTorrent (tor));
3067
3068  tr_logAddDebug ("Moving \"%s\" location from currentDir \"%s\" to \"%s\"",
3069                  tr_torrentName (tor), tor->currentDir, location);
3070
3071  tr_sys_dir_create (location, TR_SYS_DIR_CREATE_PARENTS, 0777, NULL);
3072
3073  if (!tr_sys_path_is_same (location, tor->currentDir, NULL))
3074    {
3075      tr_file_index_t i;
3076
3077      /* bad idea to move files while they're being verified... */
3078      tr_verifyRemove (tor);
3079
3080      /* try to move the files.
3081       * FIXME: there are still all kinds of nasty cases, like what
3082       * if the target directory runs out of space halfway through... */
3083      for (i=0; !err && i<tor->info.fileCount; ++i)
3084        {
3085          char * sub;
3086          const char * oldbase;
3087          const tr_file * f = &tor->info.files[i];
3088
3089          if (tr_torrentFindFile2 (tor, i, &oldbase, &sub, NULL))
3090            {
3091              char * oldpath = tr_buildPath (oldbase, sub, NULL);
3092              char * newpath = tr_buildPath (location, sub, NULL);
3093
3094              tr_logAddDebug ("Found file #%d: %s", (int)i, oldpath);
3095
3096              if (do_move && !tr_sys_path_is_same (oldpath, newpath, NULL))
3097                {
3098                  tr_error * error = NULL;
3099
3100                  tr_logAddTorInfo (tor, "moving \"%s\" to \"%s\"", oldpath, newpath);
3101                  if (!tr_moveFile (oldpath, newpath, &error))
3102                    {
3103                      err = true;
3104                      tr_logAddTorErr (tor, "error moving \"%s\" to \"%s\": %s",
3105                                       oldpath, newpath, error->message);
3106                      tr_error_free (error);
3107                    }
3108                }
3109
3110              tr_free (newpath);
3111              tr_free (oldpath);
3112              tr_free (sub);
3113            }
3114
3115          if (data->setme_progress != NULL)
3116            {
3117              bytesHandled += f->length;
3118              *data->setme_progress = bytesHandled / tor->info.totalSize;
3119            }
3120        }
3121
3122      if (!err)
3123        {
3124          /* blow away the leftover subdirectories in the old location */
3125          if (do_move)
3126            tr_torrentDeleteLocalData (tor, remove);
3127
3128          /* set the new location and reverify */
3129          tr_torrentSetDownloadDir (tor, location);
3130        }
3131    }
3132
3133  if (!err && do_move)
3134    {
3135      tr_free (tor->incompleteDir);
3136      tor->incompleteDir = NULL;
3137      tor->currentDir = tor->downloadDir;
3138    }
3139
3140  if (data->setme_state != NULL)
3141    *data->setme_state = err ? TR_LOC_ERROR : TR_LOC_DONE;
3142
3143  /* cleanup */
3144  tr_torrentUnlock (tor);
3145  tr_free (data->location);
3146  tr_free (data);
3147}
3148
3149void
3150tr_torrentSetLocation (tr_torrent       * tor,
3151                       const char       * location,
3152                       bool               move_from_old_location,
3153                       volatile double  * setme_progress,
3154                       volatile int     * setme_state)
3155{
3156  struct LocationData * data;
3157
3158  assert (tr_isTorrent (tor));
3159
3160  if (setme_state != NULL)
3161    *setme_state = TR_LOC_MOVING;
3162
3163  if (setme_progress != NULL)
3164    *setme_progress = 0;
3165
3166  /* run this in the libtransmission thread */
3167  data = tr_new (struct LocationData, 1);
3168  data->tor = tor;
3169  data->location = tr_strdup (location);
3170  data->move_from_old_location = move_from_old_location;
3171  data->setme_state = setme_state;
3172  data->setme_progress = setme_progress;
3173  tr_runInEventThread (tor->session, setLocation, data);
3174}
3175
3176/***
3177****
3178***/
3179
3180static void
3181tr_torrentFileCompleted (tr_torrent * tor, tr_file_index_t fileIndex)
3182{
3183  char * sub;
3184  const char * base;
3185  const tr_info * inf = &tor->info;
3186  const tr_file * f = &inf->files[fileIndex];
3187  tr_piece * p;
3188  const tr_piece * pend;
3189  const time_t now = tr_time ();
3190
3191  /* close the file so that we can reopen in read-only mode as needed */
3192  tr_cacheFlushFile (tor->session->cache, tor, fileIndex);
3193  tr_fdFileClose (tor->session, tor, fileIndex);
3194
3195  /* now that the file is complete and closed, we can start watching its
3196   * mtime timestamp for changes to know if we need to reverify pieces */
3197  for (p=&inf->pieces[f->firstPiece], pend=&inf->pieces[f->lastPiece]; p!=pend; ++p)
3198    p->timeChecked = now;
3199
3200  /* if the torrent's current filename isn't the same as the one in the
3201   * metadata -- for example, if it had the ".part" suffix appended to
3202   * it until now -- then rename it to match the one in the metadata */
3203  if (tr_torrentFindFile2 (tor, fileIndex, &base, &sub, NULL))
3204    {
3205      if (strcmp (sub, f->name))
3206        {
3207          char * oldpath = tr_buildPath (base, sub, NULL);
3208          char * newpath = tr_buildPath (base, f->name, NULL);
3209          tr_error * error = NULL;
3210
3211          if (!tr_sys_path_rename (oldpath, newpath, &error))
3212            {
3213              tr_logAddTorErr (tor, "Error moving \"%s\" to \"%s\": %s", oldpath, newpath, error->message);
3214              tr_error_free (error);
3215            }
3216
3217          tr_free (newpath);
3218          tr_free (oldpath);
3219        }
3220
3221      tr_free (sub);
3222    }
3223}
3224
3225static void
3226tr_torrentPieceCompleted (tr_torrent * tor, tr_piece_index_t pieceIndex)
3227{
3228  tr_file_index_t i;
3229
3230  tr_peerMgrPieceCompleted (tor, pieceIndex);
3231
3232  /* if this piece completes any file, invoke the fileCompleted func for it */
3233  for (i=0; i<tor->info.fileCount; ++i)
3234    {
3235      const tr_file * file = &tor->info.files[i];
3236
3237      if ((file->firstPiece <= pieceIndex) && (pieceIndex <= file->lastPiece))
3238        if (tr_cpFileIsComplete (&tor->completion, i))
3239          tr_torrentFileCompleted (tor, i);
3240    }
3241}
3242
3243void
3244tr_torrentGotBlock (tr_torrent * tor, tr_block_index_t block)
3245{
3246  const bool block_is_new = !tr_torrentBlockIsComplete (tor, block);
3247
3248  assert (tr_isTorrent (tor));
3249  assert (tr_amInEventThread (tor->session));
3250
3251  if (block_is_new)
3252    {
3253      tr_piece_index_t p;
3254
3255      tr_cpBlockAdd (&tor->completion, block);
3256      tr_torrentSetDirty (tor);
3257
3258      p = tr_torBlockPiece (tor, block);
3259      if (tr_torrentPieceIsComplete (tor, p))
3260        {
3261          tr_logAddTorDbg (tor, "[LAZY] checking just-completed piece %"TR_PRIuSIZE, (size_t)p);
3262
3263          if (tr_torrentCheckPiece (tor, p))
3264            {
3265              tr_torrentPieceCompleted (tor, p);
3266            }
3267          else
3268            {
3269              const uint32_t n = tr_torPieceCountBytes (tor, p);
3270              tr_logAddTorErr (tor, _("Piece %"PRIu32", which was just downloaded, failed its checksum test"), p);
3271              tor->corruptCur += n;
3272              tor->downloadedCur -= MIN (tor->downloadedCur, n);
3273              tr_peerMgrGotBadPiece (tor, p);
3274            }
3275        }
3276    }
3277  else
3278    {
3279      const uint32_t n = tr_torBlockCountBytes (tor, block);
3280      tor->downloadedCur -= MIN (tor->downloadedCur, n);
3281      tr_logAddTorDbg (tor, "we have this block already...");
3282    }
3283}
3284
3285/***
3286****
3287***/
3288
3289bool
3290tr_torrentFindFile2 (const tr_torrent * tor, tr_file_index_t fileNum,
3291                     const char ** base, char ** subpath, time_t * mtime)
3292{
3293  char * part = NULL;
3294  const tr_file * file;
3295  const char * b = NULL;
3296  const char * s = NULL;
3297  tr_sys_path_info file_info;
3298
3299  assert (tr_isTorrent (tor));
3300  assert (fileNum < tor->info.fileCount);
3301
3302  file = &tor->info.files[fileNum];
3303
3304  /* look in the download dir... */
3305  if (b == NULL)
3306    {
3307      char * filename = tr_buildPath (tor->downloadDir, file->name, NULL);
3308      if (tr_sys_path_get_info (filename, 0, &file_info, NULL))
3309        {
3310          b = tor->downloadDir;
3311          s = file->name;
3312        }
3313      tr_free (filename);
3314    }
3315
3316  /* look in the incomplete dir... */
3317  if ((b == NULL) && (tor->incompleteDir != NULL))
3318    {
3319      char * filename = tr_buildPath (tor->incompleteDir, file->name, NULL);
3320      if (tr_sys_path_get_info (filename, 0, &file_info, NULL))
3321        {
3322          b = tor->incompleteDir;
3323          s = file->name;
3324        }
3325      tr_free (filename);
3326    }
3327
3328  if (b == NULL)
3329    part = tr_torrentBuildPartial (tor, fileNum);
3330
3331  /* look for a .part file in the incomplete dir... */
3332  if ((b == NULL) && (tor->incompleteDir != NULL))
3333    {
3334      char * filename = tr_buildPath (tor->incompleteDir, part, NULL);
3335      if (tr_sys_path_get_info (filename, 0, &file_info, NULL))
3336        {
3337          b = tor->incompleteDir;
3338          s = part;
3339        }
3340      tr_free (filename);
3341    }
3342
3343  /* look for a .part file in the download dir... */
3344  if (b == NULL)
3345    {
3346      char * filename = tr_buildPath (tor->downloadDir, part, NULL);
3347      if (tr_sys_path_get_info (filename, 0, &file_info, NULL))
3348        {
3349          b = tor->downloadDir;
3350          s = part;
3351        }
3352      tr_free (filename);
3353    }
3354
3355  /* return the results */
3356  if (base != NULL)
3357    *base = b;
3358  if (subpath != NULL)
3359    *subpath = tr_strdup (s);
3360  if (mtime != NULL)
3361    *mtime = file_info.last_modified_at;
3362
3363
3364  /* cleanup */
3365  tr_free (part);
3366  return b != NULL;
3367}
3368
3369char*
3370tr_torrentFindFile (const tr_torrent * tor, tr_file_index_t fileNum)
3371{
3372  char * subpath;
3373  char * ret = NULL;
3374  const char * base;
3375
3376  if (tr_torrentFindFile2 (tor, fileNum, &base, &subpath, NULL))
3377    {
3378      ret = tr_buildPath (base, subpath, NULL);
3379      tr_free (subpath);
3380    }
3381
3382    return ret;
3383}
3384
3385/* Decide whether we should be looking for files in downloadDir or incompleteDir. */
3386static void
3387refreshCurrentDir (tr_torrent * tor)
3388{
3389  const char * dir = NULL;
3390
3391  if (tor->incompleteDir == NULL)
3392    dir = tor->downloadDir;
3393  else if (!tr_torrentHasMetadata (tor)) /* no files to find */
3394    dir = tor->incompleteDir;
3395  else if (!tr_torrentFindFile2 (tor, 0, &dir, NULL, NULL))
3396    dir = tor->incompleteDir;
3397
3398  assert (dir != NULL);
3399  assert ((dir == tor->downloadDir) || (dir == tor->incompleteDir));
3400  tor->currentDir = dir;
3401}
3402
3403char*
3404tr_torrentBuildPartial (const tr_torrent * tor, tr_file_index_t fileNum)
3405{
3406  return tr_strdup_printf ("%s.part", tor->info.files[fileNum].name);
3407}
3408
3409/***
3410****
3411***/
3412
3413static int
3414compareTorrentByQueuePosition (const void * va, const void * vb)
3415{
3416  const tr_torrent * a = * (const tr_torrent **) va;
3417  const tr_torrent * b = * (const tr_torrent **) vb;
3418
3419  return a->queuePosition - b->queuePosition;
3420}
3421
3422#ifndef NDEBUG
3423static bool
3424queueIsSequenced (tr_session * session)
3425{
3426  int i;
3427  int n;
3428  bool is_sequenced;
3429  tr_torrent ** torrents;
3430
3431  n = 0;
3432  torrents = tr_sessionGetTorrents (session, &n);
3433  qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3434
3435#if 0
3436  fprintf (stderr, "%s", "queue: ");
3437  for (i=0; i<n; ++i)
3438    fprintf (stderr, "%d ", tmp[i]->queuePosition);
3439  fputc ('\n', stderr);
3440#endif
3441
3442  /* test them */
3443  is_sequenced = true;
3444  for (i=0; is_sequenced && i<n; ++i)
3445    if (torrents[i]->queuePosition != i)
3446      is_sequenced = false;
3447
3448  tr_free (torrents);
3449  return is_sequenced;
3450}
3451#endif
3452
3453int
3454tr_torrentGetQueuePosition (const tr_torrent * tor)
3455{
3456  return tor->queuePosition;
3457}
3458
3459void
3460tr_torrentSetQueuePosition (tr_torrent * tor, int pos)
3461{
3462  int back = -1;
3463  tr_torrent * walk;
3464  const int old_pos = tor->queuePosition;
3465  const time_t now = tr_time ();
3466
3467  if (pos < 0)
3468    pos = 0;
3469
3470  tor->queuePosition = -1;
3471
3472  walk = NULL;
3473  while ((walk = tr_torrentNext (tor->session, walk)))
3474    {
3475      if (old_pos < pos)
3476        {
3477          if ((old_pos <= walk->queuePosition) && (walk->queuePosition <= pos))
3478            {
3479              walk->queuePosition--;
3480              walk->anyDate = now;
3481            }
3482        }
3483
3484      if (old_pos > pos)
3485        {
3486          if ((pos <= walk->queuePosition) && (walk->queuePosition < old_pos))
3487            {
3488              walk->queuePosition++;
3489              walk->anyDate = now;
3490            }
3491        }
3492
3493      if (back < walk->queuePosition)
3494        {
3495          back = walk->queuePosition;
3496        }
3497    }
3498
3499  tor->queuePosition = MIN (pos, (back+1));
3500  tor->anyDate = now;
3501
3502  assert (queueIsSequenced (tor->session));
3503}
3504
3505void
3506tr_torrentsQueueMoveTop (tr_torrent ** torrents_in, int n)
3507{
3508  int i;
3509  tr_torrent ** torrents = tr_memdup (torrents_in, sizeof (tr_torrent *) * n);
3510  qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3511  for (i=n-1; i>=0; --i)
3512    tr_torrentSetQueuePosition (torrents[i], 0);
3513  tr_free (torrents);
3514}
3515
3516void
3517tr_torrentsQueueMoveUp (tr_torrent ** torrents_in, int n)
3518{
3519  int i;
3520  tr_torrent ** torrents;
3521
3522  torrents = tr_memdup (torrents_in, sizeof (tr_torrent *) * n);
3523  qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3524  for (i=0; i<n; ++i)
3525    tr_torrentSetQueuePosition (torrents[i], torrents[i]->queuePosition - 1);
3526
3527  tr_free (torrents);
3528}
3529
3530void
3531tr_torrentsQueueMoveDown (tr_torrent ** torrents_in, int n)
3532{
3533  int i;
3534  tr_torrent ** torrents;
3535
3536  torrents = tr_memdup (torrents_in, sizeof (tr_torrent *) * n);
3537  qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3538  for (i=n-1; i>=0; --i)
3539    tr_torrentSetQueuePosition (torrents[i], torrents[i]->queuePosition + 1);
3540
3541  tr_free (torrents);
3542}
3543
3544void
3545tr_torrentsQueueMoveBottom (tr_torrent ** torrents_in, int n)
3546{
3547  int i;
3548  tr_torrent ** torrents;
3549
3550  torrents = tr_memdup (torrents_in, sizeof (tr_torrent *) * n);
3551  qsort (torrents, n, sizeof (tr_torrent *), compareTorrentByQueuePosition);
3552  for (i=0; i<n; ++i)
3553    tr_torrentSetQueuePosition (torrents[i], INT_MAX);
3554
3555  tr_free (torrents);
3556}
3557
3558static void
3559torrentSetQueued (tr_torrent * tor, bool queued)
3560{
3561  assert (tr_isTorrent (tor));
3562  assert (tr_isBool (queued));
3563
3564  if (tr_torrentIsQueued (tor) != queued)
3565    {
3566      tor->isQueued = queued;
3567      tor->anyDate = tr_time ();
3568      tr_torrentSetDirty (tor);
3569    }
3570}
3571
3572void
3573tr_torrentSetQueueStartCallback (tr_torrent * torrent, void (*callback)(tr_torrent *, void *), void * user_data)
3574{
3575  torrent->queue_started_callback = callback;
3576  torrent->queue_started_user_data = user_data;
3577}
3578
3579
3580/***
3581****
3582****  RENAME
3583****
3584***/
3585
3586static bool
3587renameArgsAreValid (const char * oldpath, const char * newname)
3588{
3589  return (oldpath && *oldpath)
3590      && (newname && *newname)
3591      && (strcmp (newname, "."))
3592      && (strcmp (newname, ".."))
3593      && (strchr (newname, TR_PATH_DELIMITER) == NULL);
3594}
3595
3596static tr_file_index_t *
3597renameFindAffectedFiles (tr_torrent * tor, const char * oldpath, size_t * setme_n)
3598{
3599  size_t n;
3600  size_t oldpath_len;
3601  tr_file_index_t i;
3602  tr_file_index_t * indices = tr_new0 (tr_file_index_t, tor->info.fileCount);
3603
3604  n = 0;
3605  oldpath_len = strlen (oldpath);
3606  for (i=0; i!=tor->info.fileCount; ++i)
3607    {
3608      const char * name = tor->info.files[i].name;
3609      const size_t len = strlen (name);
3610      if ((len == oldpath_len || (len > oldpath_len && name[oldpath_len] == '/')) &&
3611          !memcmp (oldpath, name, oldpath_len))
3612        indices[n++] = i;
3613    }
3614
3615  *setme_n = n;
3616  return indices;
3617}
3618
3619static int
3620renamePath (tr_torrent  * tor,
3621            const char  * oldpath,
3622            const char  * newname)
3623{
3624  char * src;
3625  const char * base;
3626  int err = 0;
3627
3628  if (!tr_torrentIsSeed(tor) && (tor->incompleteDir != NULL))
3629    base = tor->incompleteDir;
3630  else
3631    base = tor->downloadDir;
3632
3633  src = tr_buildPath (base, oldpath, NULL);
3634  if (!tr_sys_path_exists (src, NULL)) /* check for it as a partial */
3635    {
3636      char * tmp = tr_strdup_printf ("%s.part", src);
3637      tr_free (src);
3638      src = tmp;
3639    }
3640
3641  if (tr_sys_path_exists (src, NULL))
3642    {
3643      int tmp;
3644      bool tgt_exists;
3645      char * parent = tr_sys_path_dirname (src, NULL);
3646      char * tgt;
3647
3648      if (tr_str_has_suffix (src, ".part"))
3649        tgt = tr_strdup_printf ("%s" TR_PATH_DELIMITER_STR "%s.part", parent, newname);
3650      else
3651        tgt = tr_buildPath (parent, newname, NULL);
3652
3653      tmp = errno;
3654      tgt_exists = tr_sys_path_exists (tgt, NULL);
3655      errno = tmp;
3656
3657      if (!tgt_exists)
3658        {
3659          tr_error * error = NULL;
3660
3661          tmp = errno;
3662          if (!tr_sys_path_rename (src, tgt, &error))
3663            {
3664              err = error->code;
3665              tr_error_free (error);
3666            }
3667          errno = tmp;
3668        }
3669
3670      tr_free (tgt);
3671      tr_free (parent);
3672    }
3673
3674  tr_free (src);
3675
3676  return err;
3677}
3678
3679static void
3680renameTorrentFileString (tr_torrent       * tor,
3681                         const char       * oldpath,
3682                         const char       * newname,
3683                         tr_file_index_t    fileIndex)
3684{
3685  char * name;
3686  tr_file * file = &tor->info.files[fileIndex];
3687  const size_t oldpath_len = strlen (oldpath);
3688
3689  if (strchr (oldpath, TR_PATH_DELIMITER) == NULL)
3690    {
3691      if (oldpath_len >= strlen(file->name))
3692        name = tr_buildPath (newname, NULL);
3693      else
3694        name = tr_buildPath (newname, file->name + oldpath_len + 1, NULL);
3695    }
3696  else
3697    {
3698      char * tmp = tr_sys_path_dirname (oldpath, NULL);
3699
3700      if (oldpath_len >= strlen(file->name))
3701        name = tr_buildPath (tmp, newname, NULL);
3702      else
3703        name = tr_buildPath (tmp, newname, file->name + oldpath_len + 1, NULL);
3704
3705      tr_free (tmp);
3706    }
3707
3708  if (!strcmp (file->name, name))
3709    {
3710      tr_free (name);
3711    }
3712  else
3713    {
3714      tr_free (file->name);
3715      file->name = name;
3716      file->is_renamed = true;
3717    }
3718}
3719
3720struct rename_data
3721{
3722  tr_torrent * tor;
3723  char * oldpath;
3724  char * newname;
3725  tr_torrent_rename_done_func callback;
3726  void * callback_user_data;
3727};
3728
3729static void
3730torrentRenamePath (void * vdata)
3731{
3732  int error = 0;
3733  struct rename_data * data = vdata;
3734  tr_torrent * const tor = data->tor;
3735  const char * const oldpath = data->oldpath;
3736  const char * const newname = data->newname;
3737
3738  /***
3739  ****
3740  ***/
3741
3742  assert (tr_isTorrent (tor));
3743
3744  if (!renameArgsAreValid (oldpath, newname))
3745    {
3746      error = EINVAL;
3747    }
3748  else
3749    {
3750      size_t n;
3751      tr_file_index_t * file_indices;
3752
3753      file_indices = renameFindAffectedFiles (tor, oldpath, &n);
3754      if (n == 0)
3755        {
3756          error = EINVAL;
3757        }
3758      else
3759        {
3760          size_t i;
3761
3762          error = renamePath (tor, oldpath, newname);
3763
3764          if (!error)
3765            {
3766              /* update tr_info.files */
3767              for (i=0; i<n; ++i)
3768                renameTorrentFileString(tor, oldpath, newname, file_indices[i]);
3769
3770              /* update tr_info.name if user changed the toplevel */
3771              if ((n == tor->info.fileCount) && (strchr(oldpath,'/')==NULL))
3772                {
3773                  tr_free (tor->info.name);
3774                  tor->info.name = tr_strdup (newname);
3775                }
3776
3777              tr_torrentSetDirty (tor);
3778            }
3779        }
3780
3781      tr_free (file_indices);
3782    }
3783
3784
3785  /***
3786  ****
3787  ***/
3788
3789  tor->anyDate = tr_time ();
3790
3791  /* callback */
3792  if (data->callback != NULL)
3793    (*data->callback)(tor, data->oldpath, data->newname, error, data->callback_user_data);
3794
3795  /* cleanup */
3796  tr_free (data->oldpath);
3797  tr_free (data->newname);
3798  tr_free (data);
3799}
3800
3801void
3802tr_torrentRenamePath (tr_torrent                  * tor,
3803                      const char                  * oldpath,
3804                      const char                  * newname,
3805                      tr_torrent_rename_done_func   callback,
3806                      void                        * callback_user_data)
3807{
3808  struct rename_data * data;
3809
3810  data = tr_new0 (struct rename_data, 1);
3811  data->tor = tor;
3812  data->oldpath = tr_strdup (oldpath);
3813  data->newname = tr_strdup (newname);
3814  data->callback = callback;
3815  data->callback_user_data = callback_user_data;
3816
3817  tr_runInEventThread (tor->session, torrentRenamePath, data);
3818}
Note: See TracBrowser for help on using the repository browser.