source: trunk/libtransmission/torrent.c @ 14382

Last change on this file since 14382 was 14382, checked in by mikedld, 8 years ago

Fix compilation on Windows

This should not affect non-Win32 platforms in any way.
As for Win32 (both MinGW and MSVC), this should hopefully allow for
unpatched compilation. Correct functioning is not yet guaranteed though.

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