source: trunk/libtransmission/torrent.c @ 14148

Last change on this file since 14148 was 14148, checked in by jordan, 9 years ago

silence a pair of 'unused variable' gcc warnings

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