source: trunk/libtransmission/session.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: 75.3 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: session.c 14148 2013-07-27 17:48:59Z jordan $
11 */
12
13#include <assert.h>
14#include <errno.h> /* ENOENT */
15#include <stdlib.h>
16#include <string.h> /* memcpy */
17
18#include <signal.h>
19#include <sys/types.h> /* stat (), umask () */
20#include <sys/stat.h> /* stat (), umask () */
21#include <unistd.h> /* stat */
22#include <dirent.h> /* opendir */
23
24#include <event2/dns.h> /* evdns_base_free () */
25#include <event2/event.h>
26
27#include <libutp/utp.h>
28
29//#define TR_SHOW_DEPRECATED
30#include "transmission.h"
31#include "announcer.h"
32#include "bandwidth.h"
33#include "blocklist.h"
34#include "cache.h"
35#include "crypto.h"
36#include "fdlimit.h"
37#include "list.h"
38#include "log.h"
39#include "net.h"
40#include "peer-io.h"
41#include "peer-mgr.h"
42#include "platform.h" /* tr_lock, tr_getTorrentDir () */
43#include "platform-quota.h" /* tr_device_info_free() */
44#include "port-forwarding.h"
45#include "rpc-server.h"
46#include "session.h"
47#include "stats.h"
48#include "torrent.h"
49#include "tr-dht.h" /* tr_dhtUpkeep () */
50#include "tr-udp.h"
51#include "tr-utp.h"
52#include "tr-lpd.h"
53#include "trevent.h"
54#include "utils.h"
55#include "variant.h"
56#include "verify.h"
57#include "version.h"
58#include "web.h"
59
60enum
61{
62#ifdef TR_LIGHTWEIGHT
63  DEFAULT_CACHE_SIZE_MB = 2,
64  DEFAULT_PREFETCH_ENABLED = false,
65#else
66  DEFAULT_CACHE_SIZE_MB = 4,
67  DEFAULT_PREFETCH_ENABLED = true,
68#endif
69  SAVE_INTERVAL_SECS = 360
70};
71
72
73#define dbgmsg(...) \
74  do \
75    { \
76      if (tr_logGetDeepEnabled ()) \
77        tr_logAddDeep (__FILE__, __LINE__, NULL, __VA_ARGS__); \
78    } \
79  while (0)
80
81static tr_port
82getRandomPort (tr_session * s)
83{
84  return tr_cryptoWeakRandInt (s->randomPortHigh - s->randomPortLow + 1) + s->randomPortLow;
85}
86
87/* Generate a peer id : "-TRxyzb-" + 12 random alphanumeric
88   characters, where x is the major version number, y is the
89   minor version number, z is the maintenance number, and b
90   designates beta (Azureus-style) */
91void
92tr_peerIdInit (uint8_t * buf)
93{
94  int i;
95  int val;
96  int total = 0;
97  const char * pool = "0123456789abcdefghijklmnopqrstuvwxyz";
98  const int base = 36;
99
100  memcpy (buf, PEERID_PREFIX, 8);
101
102  tr_cryptoRandBuf (buf+8, 11);
103  for (i=8; i<19; ++i)
104    {
105      val = buf[i] % base;
106      total += val;
107      buf[i] = pool[val];
108    }
109
110  val = total % base ? base - (total % base) : 0;
111  buf[19] = pool[val];
112  buf[20] = '\0';
113}
114
115/***
116****
117***/
118
119tr_encryption_mode
120tr_sessionGetEncryption (tr_session * session)
121{
122  assert (session);
123
124  return session->encryptionMode;
125}
126
127void
128tr_sessionSetEncryption (tr_session          * session,
129                         tr_encryption_mode    mode)
130{
131  assert (session);
132  assert (mode == TR_ENCRYPTION_PREFERRED
133       || mode == TR_ENCRYPTION_REQUIRED
134       || mode == TR_CLEAR_PREFERRED);
135
136  session->encryptionMode = mode;
137}
138
139/***
140****
141***/
142
143struct tr_bindinfo
144{
145  int socket;
146  tr_address addr;
147  struct event * ev;
148};
149
150
151static void
152close_bindinfo (struct tr_bindinfo * b)
153{
154  if ((b != NULL) && (b->socket >=0))
155    {
156      event_free (b->ev);
157      b->ev = NULL;
158      tr_netCloseSocket (b->socket);
159    }
160}
161
162static void
163close_incoming_peer_port (tr_session * session)
164{
165  close_bindinfo (session->public_ipv4);
166  close_bindinfo (session->public_ipv6);
167}
168
169static void
170free_incoming_peer_port (tr_session * session)
171{
172  close_bindinfo (session->public_ipv4);
173  tr_free (session->public_ipv4);
174  session->public_ipv4 = NULL;
175
176  close_bindinfo (session->public_ipv6);
177  tr_free (session->public_ipv6);
178  session->public_ipv6 = NULL;
179}
180
181static void
182accept_incoming_peer (int fd, short what UNUSED, void * vsession)
183{
184  int clientSocket;
185  tr_port clientPort;
186  tr_address clientAddr;
187  tr_session * session = vsession;
188
189  clientSocket = tr_netAccept (session, fd, &clientAddr, &clientPort);
190  if (clientSocket > 0)
191    {
192      tr_logAddDeep (__FILE__, __LINE__, NULL, "new incoming connection %d (%s)",
193                       clientSocket, tr_peerIoAddrStr (&clientAddr, clientPort));
194      tr_peerMgrAddIncoming (session->peerMgr, &clientAddr, clientPort,
195                             clientSocket, NULL);
196    }
197}
198
199static void
200open_incoming_peer_port (tr_session * session)
201{
202  struct tr_bindinfo * b;
203
204  /* bind an ipv4 port to listen for incoming peers... */
205  b = session->public_ipv4;
206  b->socket = tr_netBindTCP (&b->addr, session->private_peer_port, false);
207  if (b->socket >= 0)
208    {
209      b->ev = event_new (session->event_base, b->socket, EV_READ | EV_PERSIST, accept_incoming_peer, session);
210      event_add (b->ev, NULL);
211    }
212
213  /* and do the exact same thing for ipv6, if it's supported... */
214  if (tr_net_hasIPv6 (session->private_peer_port))
215    {
216      b = session->public_ipv6;
217      b->socket = tr_netBindTCP (&b->addr, session->private_peer_port, false);
218      if (b->socket >= 0)
219        {
220          b->ev = event_new (session->event_base, b->socket, EV_READ | EV_PERSIST, accept_incoming_peer, session);
221          event_add (b->ev, NULL);
222        }
223    }
224}
225
226const tr_address*
227tr_sessionGetPublicAddress (const tr_session * session, int tr_af_type, bool * is_default_value)
228{
229  const char * default_value;
230  const struct tr_bindinfo * bindinfo;
231
232  switch (tr_af_type)
233    {
234      case TR_AF_INET:
235        bindinfo = session->public_ipv4;
236        default_value = TR_DEFAULT_BIND_ADDRESS_IPV4;
237        break;
238
239      case TR_AF_INET6:
240        bindinfo = session->public_ipv6;
241        default_value = TR_DEFAULT_BIND_ADDRESS_IPV6;
242        break;
243
244      default:
245        bindinfo = NULL;
246        default_value = "";
247        break;
248    }
249
250  if (is_default_value && bindinfo)
251    *is_default_value = !tr_strcmp0 (default_value, tr_address_to_string (&bindinfo->addr));
252
253  return bindinfo ? &bindinfo->addr : NULL;
254}
255
256/***
257****
258***/
259
260#ifdef TR_LIGHTWEIGHT
261 #define TR_DEFAULT_ENCRYPTION   TR_CLEAR_PREFERRED
262#else
263 #define TR_DEFAULT_ENCRYPTION   TR_ENCRYPTION_PREFERRED
264#endif
265
266static int
267parse_tos (const char *str)
268{
269  char *p;
270  int value;
271
272  if (!evutil_ascii_strcasecmp (str, ""))
273    return 0;
274  if (!evutil_ascii_strcasecmp (str, "default"))
275    return 0;
276
277  if (!evutil_ascii_strcasecmp (str, "lowcost"))
278    return 0x10;
279  if (!evutil_ascii_strcasecmp (str, "mincost"))
280    return 0x10;
281
282  if (!evutil_ascii_strcasecmp (str, "throughput"))
283    return 0x08;
284  if (!evutil_ascii_strcasecmp (str, "reliability"))
285    return 0x04;
286  if (!evutil_ascii_strcasecmp (str, "lowdelay"))
287    return 0x02;
288
289  value = strtol (str, &p, 0);
290  if (!p || (p == str))
291    return 0;
292
293  return value;
294}
295
296static const char *
297format_tos (int value)
298{
299  static char buf[8];
300  switch (value)
301    {
302      case 0: return "default";
303      case 0x10: return "lowcost";
304      case 0x08: return "throughput";
305      case 0x04: return "reliability";
306      case 0x02: return "lowdelay";
307      default:
308        tr_snprintf (buf, 8, "%d", value);
309        return buf;
310    }
311}
312
313void
314tr_sessionGetDefaultSettings (tr_variant * d)
315{
316  assert (tr_variantIsDict (d));
317
318  tr_variantDictReserve (d, 63);
319  tr_variantDictAddBool (d, TR_KEY_blocklist_enabled,               false);
320  tr_variantDictAddStr  (d, TR_KEY_blocklist_url,                   "http://www.example.com/blocklist");
321  tr_variantDictAddInt  (d, TR_KEY_cache_size_mb,                   DEFAULT_CACHE_SIZE_MB);
322  tr_variantDictAddBool (d, TR_KEY_dht_enabled,                     true);
323  tr_variantDictAddBool (d, TR_KEY_utp_enabled,                     true);
324  tr_variantDictAddBool (d, TR_KEY_lpd_enabled,                     false);
325  tr_variantDictAddStr  (d, TR_KEY_download_dir,                    tr_getDefaultDownloadDir ());
326  tr_variantDictAddInt  (d, TR_KEY_speed_limit_down,                100);
327  tr_variantDictAddBool (d, TR_KEY_speed_limit_down_enabled,        false);
328  tr_variantDictAddInt  (d, TR_KEY_encryption,                      TR_DEFAULT_ENCRYPTION);
329  tr_variantDictAddInt  (d, TR_KEY_idle_seeding_limit,              30);
330  tr_variantDictAddBool (d, TR_KEY_idle_seeding_limit_enabled,      false);
331  tr_variantDictAddStr  (d, TR_KEY_incomplete_dir,                  tr_getDefaultDownloadDir ());
332  tr_variantDictAddBool (d, TR_KEY_incomplete_dir_enabled,          false);
333  tr_variantDictAddInt  (d, TR_KEY_message_level,                   TR_LOG_INFO);
334  tr_variantDictAddInt  (d, TR_KEY_download_queue_size,             5);
335  tr_variantDictAddBool (d, TR_KEY_download_queue_enabled,          true);
336  tr_variantDictAddInt  (d, TR_KEY_peer_limit_global,               atoi (TR_DEFAULT_PEER_LIMIT_GLOBAL_STR));
337  tr_variantDictAddInt  (d, TR_KEY_peer_limit_per_torrent,          atoi (TR_DEFAULT_PEER_LIMIT_TORRENT_STR));
338  tr_variantDictAddInt  (d, TR_KEY_peer_port,                       atoi (TR_DEFAULT_PEER_PORT_STR));
339  tr_variantDictAddBool (d, TR_KEY_peer_port_random_on_start,       false);
340  tr_variantDictAddInt  (d, TR_KEY_peer_port_random_low,            49152);
341  tr_variantDictAddInt  (d, TR_KEY_peer_port_random_high,           65535);
342  tr_variantDictAddStr  (d, TR_KEY_peer_socket_tos,                 TR_DEFAULT_PEER_SOCKET_TOS_STR);
343  tr_variantDictAddBool (d, TR_KEY_pex_enabled,                     true);
344  tr_variantDictAddBool (d, TR_KEY_port_forwarding_enabled,         true);
345  tr_variantDictAddInt  (d, TR_KEY_preallocation,                   TR_PREALLOCATE_SPARSE);
346  tr_variantDictAddBool (d, TR_KEY_prefetch_enabled,                DEFAULT_PREFETCH_ENABLED);
347  tr_variantDictAddInt  (d, TR_KEY_peer_id_ttl_hours,               6);
348  tr_variantDictAddBool (d, TR_KEY_queue_stalled_enabled,           true);
349  tr_variantDictAddInt  (d, TR_KEY_queue_stalled_minutes,           30);
350  tr_variantDictAddReal (d, TR_KEY_ratio_limit,                     2.0);
351  tr_variantDictAddBool (d, TR_KEY_ratio_limit_enabled,             false);
352  tr_variantDictAddBool (d, TR_KEY_rename_partial_files,            true);
353  tr_variantDictAddBool (d, TR_KEY_rpc_authentication_required,     false);
354  tr_variantDictAddStr  (d, TR_KEY_rpc_bind_address,                "0.0.0.0");
355  tr_variantDictAddBool (d, TR_KEY_rpc_enabled,                     false);
356  tr_variantDictAddStr  (d, TR_KEY_rpc_password,                    "");
357  tr_variantDictAddStr  (d, TR_KEY_rpc_username,                    "");
358  tr_variantDictAddStr  (d, TR_KEY_rpc_whitelist,                   TR_DEFAULT_RPC_WHITELIST);
359  tr_variantDictAddBool (d, TR_KEY_rpc_whitelist_enabled,           true);
360  tr_variantDictAddInt  (d, TR_KEY_rpc_port,                        atoi (TR_DEFAULT_RPC_PORT_STR));
361  tr_variantDictAddStr  (d, TR_KEY_rpc_url,                         TR_DEFAULT_RPC_URL_STR);
362  tr_variantDictAddBool (d, TR_KEY_scrape_paused_torrents_enabled,  true);
363  tr_variantDictAddStr  (d, TR_KEY_script_torrent_done_filename,    "");
364  tr_variantDictAddBool (d, TR_KEY_script_torrent_done_enabled,     false);
365  tr_variantDictAddInt  (d, TR_KEY_seed_queue_size,                 10);
366  tr_variantDictAddBool (d, TR_KEY_seed_queue_enabled,              false);
367  tr_variantDictAddBool (d, TR_KEY_alt_speed_enabled,               false);
368  tr_variantDictAddInt  (d, TR_KEY_alt_speed_up,                    50); /* half the regular */
369  tr_variantDictAddInt  (d, TR_KEY_alt_speed_down,                  50); /* half the regular */
370  tr_variantDictAddInt  (d, TR_KEY_alt_speed_time_begin,            540); /* 9am */
371  tr_variantDictAddBool (d, TR_KEY_alt_speed_time_enabled,          false);
372  tr_variantDictAddInt  (d, TR_KEY_alt_speed_time_end,              1020); /* 5pm */
373  tr_variantDictAddInt  (d, TR_KEY_alt_speed_time_day,              TR_SCHED_ALL);
374  tr_variantDictAddInt  (d, TR_KEY_speed_limit_up,                  100);
375  tr_variantDictAddBool (d, TR_KEY_speed_limit_up_enabled,          false);
376  tr_variantDictAddInt  (d, TR_KEY_umask,                           022);
377  tr_variantDictAddInt  (d, TR_KEY_upload_slots_per_torrent,        14);
378  tr_variantDictAddStr  (d, TR_KEY_bind_address_ipv4,               TR_DEFAULT_BIND_ADDRESS_IPV4);
379  tr_variantDictAddStr  (d, TR_KEY_bind_address_ipv6,               TR_DEFAULT_BIND_ADDRESS_IPV6);
380  tr_variantDictAddBool (d, TR_KEY_start_added_torrents,            true);
381  tr_variantDictAddBool (d, TR_KEY_trash_original_torrent_files,    false);
382}
383
384void
385tr_sessionGetSettings (tr_session * s, tr_variant * d)
386{
387  assert (tr_variantIsDict (d));
388
389  tr_variantDictReserve (d, 63);
390  tr_variantDictAddBool (d, TR_KEY_blocklist_enabled,            tr_blocklistIsEnabled (s));
391  tr_variantDictAddStr  (d, TR_KEY_blocklist_url,                tr_blocklistGetURL (s));
392  tr_variantDictAddInt  (d, TR_KEY_cache_size_mb,                tr_sessionGetCacheLimit_MB (s));
393  tr_variantDictAddBool (d, TR_KEY_dht_enabled,                  s->isDHTEnabled);
394  tr_variantDictAddBool (d, TR_KEY_utp_enabled,                  s->isUTPEnabled);
395  tr_variantDictAddBool (d, TR_KEY_lpd_enabled,                  s->isLPDEnabled);
396  tr_variantDictAddStr  (d, TR_KEY_download_dir,                 tr_sessionGetDownloadDir (s));
397  tr_variantDictAddInt  (d, TR_KEY_download_queue_size,          tr_sessionGetQueueSize (s, TR_DOWN));
398  tr_variantDictAddBool (d, TR_KEY_download_queue_enabled,       tr_sessionGetQueueEnabled (s, TR_DOWN));
399  tr_variantDictAddInt  (d, TR_KEY_speed_limit_down,             tr_sessionGetSpeedLimit_KBps (s, TR_DOWN));
400  tr_variantDictAddBool (d, TR_KEY_speed_limit_down_enabled,     tr_sessionIsSpeedLimited (s, TR_DOWN));
401  tr_variantDictAddInt  (d, TR_KEY_encryption,                   s->encryptionMode);
402  tr_variantDictAddInt  (d, TR_KEY_idle_seeding_limit,           tr_sessionGetIdleLimit (s));
403  tr_variantDictAddBool (d, TR_KEY_idle_seeding_limit_enabled,   tr_sessionIsIdleLimited (s));
404  tr_variantDictAddStr  (d, TR_KEY_incomplete_dir,               tr_sessionGetIncompleteDir (s));
405  tr_variantDictAddBool (d, TR_KEY_incomplete_dir_enabled,       tr_sessionIsIncompleteDirEnabled (s));
406  tr_variantDictAddInt  (d, TR_KEY_message_level,                tr_logGetLevel ());
407  tr_variantDictAddInt  (d, TR_KEY_peer_limit_global,            s->peerLimit);
408  tr_variantDictAddInt  (d, TR_KEY_peer_limit_per_torrent,       s->peerLimitPerTorrent);
409  tr_variantDictAddInt  (d, TR_KEY_peer_port,                    tr_sessionGetPeerPort (s));
410  tr_variantDictAddBool (d, TR_KEY_peer_port_random_on_start,    s->isPortRandom);
411  tr_variantDictAddInt  (d, TR_KEY_peer_port_random_low,         s->randomPortLow);
412  tr_variantDictAddInt  (d, TR_KEY_peer_port_random_high,        s->randomPortHigh);
413  tr_variantDictAddStr  (d, TR_KEY_peer_socket_tos,              format_tos (s->peerSocketTOS));
414  tr_variantDictAddStr  (d, TR_KEY_peer_congestion_algorithm,    s->peer_congestion_algorithm);
415  tr_variantDictAddBool (d, TR_KEY_pex_enabled,                  s->isPexEnabled);
416  tr_variantDictAddBool (d, TR_KEY_port_forwarding_enabled,      tr_sessionIsPortForwardingEnabled (s));
417  tr_variantDictAddInt  (d, TR_KEY_preallocation,                s->preallocationMode);
418  tr_variantDictAddInt  (d, TR_KEY_prefetch_enabled,             s->isPrefetchEnabled);
419  tr_variantDictAddInt  (d, TR_KEY_peer_id_ttl_hours,            s->peer_id_ttl_hours);
420  tr_variantDictAddBool (d, TR_KEY_queue_stalled_enabled,        tr_sessionGetQueueStalledEnabled (s));
421  tr_variantDictAddInt  (d, TR_KEY_queue_stalled_minutes,        tr_sessionGetQueueStalledMinutes (s));
422  tr_variantDictAddReal (d, TR_KEY_ratio_limit,                  s->desiredRatio);
423  tr_variantDictAddBool (d, TR_KEY_ratio_limit_enabled,          s->isRatioLimited);
424  tr_variantDictAddBool (d, TR_KEY_rename_partial_files,         tr_sessionIsIncompleteFileNamingEnabled (s));
425  tr_variantDictAddBool (d, TR_KEY_rpc_authentication_required,  tr_sessionIsRPCPasswordEnabled (s));
426  tr_variantDictAddStr  (d, TR_KEY_rpc_bind_address,             tr_sessionGetRPCBindAddress (s));
427  tr_variantDictAddBool (d, TR_KEY_rpc_enabled,                  tr_sessionIsRPCEnabled (s));
428  tr_variantDictAddStr  (d, TR_KEY_rpc_password,                 tr_sessionGetRPCPassword (s));
429  tr_variantDictAddInt  (d, TR_KEY_rpc_port,                     tr_sessionGetRPCPort (s));
430  tr_variantDictAddStr  (d, TR_KEY_rpc_url,                      tr_sessionGetRPCUrl (s));
431  tr_variantDictAddStr  (d, TR_KEY_rpc_username,                 tr_sessionGetRPCUsername (s));
432  tr_variantDictAddStr  (d, TR_KEY_rpc_whitelist,                tr_sessionGetRPCWhitelist (s));
433  tr_variantDictAddBool (d, TR_KEY_rpc_whitelist_enabled,        tr_sessionGetRPCWhitelistEnabled (s));
434  tr_variantDictAddBool (d, TR_KEY_scrape_paused_torrents_enabled, s->scrapePausedTorrents);
435  tr_variantDictAddBool (d, TR_KEY_script_torrent_done_enabled,  tr_sessionIsTorrentDoneScriptEnabled (s));
436  tr_variantDictAddStr  (d, TR_KEY_script_torrent_done_filename, tr_sessionGetTorrentDoneScript (s));
437  tr_variantDictAddInt  (d, TR_KEY_seed_queue_size,              tr_sessionGetQueueSize (s, TR_UP));
438  tr_variantDictAddBool (d, TR_KEY_seed_queue_enabled,           tr_sessionGetQueueEnabled (s, TR_UP));
439  tr_variantDictAddBool (d, TR_KEY_alt_speed_enabled,            tr_sessionUsesAltSpeed (s));
440  tr_variantDictAddInt  (d, TR_KEY_alt_speed_up,                 tr_sessionGetAltSpeed_KBps (s, TR_UP));
441  tr_variantDictAddInt  (d, TR_KEY_alt_speed_down,               tr_sessionGetAltSpeed_KBps (s, TR_DOWN));
442  tr_variantDictAddInt  (d, TR_KEY_alt_speed_time_begin,         tr_sessionGetAltSpeedBegin (s));
443  tr_variantDictAddBool (d, TR_KEY_alt_speed_time_enabled,       tr_sessionUsesAltSpeedTime (s));
444  tr_variantDictAddInt  (d, TR_KEY_alt_speed_time_end,           tr_sessionGetAltSpeedEnd (s));
445  tr_variantDictAddInt  (d, TR_KEY_alt_speed_time_day,           tr_sessionGetAltSpeedDay (s));
446  tr_variantDictAddInt  (d, TR_KEY_speed_limit_up,               tr_sessionGetSpeedLimit_KBps (s, TR_UP));
447  tr_variantDictAddBool (d, TR_KEY_speed_limit_up_enabled,       tr_sessionIsSpeedLimited (s, TR_UP));
448  tr_variantDictAddInt  (d, TR_KEY_umask,                        s->umask);
449  tr_variantDictAddInt  (d, TR_KEY_upload_slots_per_torrent,     s->uploadSlotsPerTorrent);
450  tr_variantDictAddStr  (d, TR_KEY_bind_address_ipv4,            tr_address_to_string (&s->public_ipv4->addr));
451  tr_variantDictAddStr  (d, TR_KEY_bind_address_ipv6,            tr_address_to_string (&s->public_ipv6->addr));
452  tr_variantDictAddBool (d, TR_KEY_start_added_torrents,         !tr_sessionGetPaused (s));
453  tr_variantDictAddBool (d, TR_KEY_trash_original_torrent_files, tr_sessionGetDeleteSource (s));
454}
455
456bool
457tr_sessionLoadSettings (tr_variant * dict, const char * configDir, const char * appName)
458{
459  int err = 0;
460  char * filename;
461  tr_variant fileSettings;
462  tr_variant sessionDefaults;
463  tr_variant tmp;
464  bool success = false;
465
466  assert (tr_variantIsDict (dict));
467
468  /* initializing the defaults: caller may have passed in some app-level defaults.
469   * preserve those and use the session defaults to fill in any missing gaps. */
470  tr_variantInitDict (&sessionDefaults, 0);
471  tr_sessionGetDefaultSettings (&sessionDefaults);
472  tr_variantMergeDicts (&sessionDefaults, dict);
473  tmp = *dict;
474  *dict = sessionDefaults;
475  sessionDefaults = tmp;
476
477  /* if caller didn't specify a config dir, use the default */
478  if (!configDir || !*configDir)
479    configDir = tr_getDefaultConfigDir (appName);
480
481  /* file settings override the defaults */
482  filename = tr_buildPath (configDir, "settings.json", NULL);
483  err = tr_variantFromFile (&fileSettings, TR_VARIANT_FMT_JSON, filename);
484  if (!err)
485    {
486      tr_variantMergeDicts (dict, &fileSettings);
487      tr_variantFree (&fileSettings);
488    }
489
490  /* cleanup */
491  tr_variantFree (&sessionDefaults);
492  tr_free (filename);
493  success = (err==0) || (err==ENOENT);
494  return success;
495}
496
497void
498tr_sessionSaveSettings (tr_session       * session,
499                        const char       * configDir,
500                        const tr_variant * clientSettings)
501{
502  tr_variant settings;
503  char * filename = tr_buildPath (configDir, "settings.json", NULL);
504
505  assert (tr_variantIsDict (clientSettings));
506
507  tr_variantInitDict (&settings, 0);
508
509  /* the existing file settings are the fallback values */
510  {
511    tr_variant fileSettings;
512    const int err = tr_variantFromFile (&fileSettings, TR_VARIANT_FMT_JSON, filename);
513    if (!err)
514      {
515        tr_variantMergeDicts (&settings, &fileSettings);
516        tr_variantFree (&fileSettings);
517      }
518  }
519
520  /* the client's settings override the file settings */
521  tr_variantMergeDicts (&settings, clientSettings);
522
523  /* the session's true values override the file & client settings */
524  {
525    tr_variant sessionSettings;
526    tr_variantInitDict (&sessionSettings, 0);
527    tr_sessionGetSettings (session, &sessionSettings);
528    tr_variantMergeDicts (&settings, &sessionSettings);
529    tr_variantFree (&sessionSettings);
530  }
531
532  /* save the result */
533  tr_variantToFile (&settings, TR_VARIANT_FMT_JSON, filename);
534
535  /* cleanup */
536  tr_free (filename);
537  tr_variantFree (&settings);
538}
539
540/***
541****
542***/
543
544/**
545 * Periodically save the .resume files of any torrents whose
546 * status has recently changed. This prevents loss of metadata
547 * in the case of a crash, unclean shutdown, clumsy user, etc.
548 */
549static void
550onSaveTimer (int foo UNUSED, short bar UNUSED, void * vsession)
551{
552  tr_torrent * tor = NULL;
553  tr_session * session = vsession;
554
555  if (tr_cacheFlushDone (session->cache))
556    tr_logAddError ("Error while flushing completed pieces from cache");
557
558  while ((tor = tr_torrentNext (session, tor)))
559    tr_torrentSave (tor);
560
561  tr_statsSaveDirty (session);
562
563  tr_timerAdd (session->saveTimer, SAVE_INTERVAL_SECS, 0);
564}
565
566/***
567****
568***/
569
570static void tr_sessionInitImpl (void *);
571
572struct init_data
573{
574  bool done;
575  bool messageQueuingEnabled;
576  tr_session * session;
577  const char * configDir;
578  tr_variant * clientSettings;
579};
580
581tr_session *
582tr_sessionInit (const char  * tag,
583                const char  * configDir,
584                bool          messageQueuingEnabled,
585                tr_variant     * clientSettings)
586{
587  int64_t i;
588  tr_session * session;
589  struct init_data data;
590
591  assert (tr_variantIsDict (clientSettings));
592
593  tr_timeUpdate (time (NULL));
594
595  /* initialize the bare skeleton of the session object */
596  session = tr_new0 (tr_session, 1);
597  session->udp_socket = -1;
598  session->udp6_socket = -1;
599  session->lock = tr_lockNew ();
600  session->cache = tr_cacheNew (1024*1024*2);
601  session->tag = tr_strdup (tag);
602  session->magicNumber = SESSION_MAGIC_NUMBER;
603  tr_bandwidthConstruct (&session->bandwidth, session, NULL);
604  tr_variantInitList (&session->removedTorrents, 0);
605
606  /* nice to start logging at the very beginning */
607  if (tr_variantDictFindInt (clientSettings, TR_KEY_message_level, &i))
608    tr_logSetLevel (i);
609
610  /* start the libtransmission thread */
611  tr_netInit (); /* must go before tr_eventInit */
612  tr_eventInit (session);
613  assert (session->events != NULL);
614
615  /* run the rest in the libtransmission thread */
616  data.done = false;
617  data.session = session;
618  data.configDir = configDir;
619  data.messageQueuingEnabled = messageQueuingEnabled;
620  data.clientSettings = clientSettings;
621  tr_runInEventThread (session, tr_sessionInitImpl, &data);
622  while (!data.done)
623    tr_wait_msec (50);
624
625  return session;
626}
627
628static void turtleCheckClock (tr_session * s, struct tr_turtle_info * t);
629
630static void
631onNowTimer (int foo UNUSED, short bar UNUSED, void * vsession)
632{
633  int usec;
634  const int min = 100;
635  const int max = 999999;
636  struct timeval tv;
637  tr_torrent * tor = NULL;
638  tr_session * session = vsession;
639  const time_t now = time (NULL);
640
641  assert (tr_isSession (session));
642  assert (session->nowTimer != NULL);
643
644  /**
645  ***  tr_session things to do once per second
646  **/
647
648  tr_timeUpdate (now);
649
650  tr_dhtUpkeep (session);
651
652  if (session->turtle.isClockEnabled)
653    turtleCheckClock (session, &session->turtle);
654
655  while ((tor = tr_torrentNext (session, tor)))
656    {
657      if (tor->isRunning)
658        {
659          if (tr_torrentIsSeed (tor))
660            ++tor->secondsSeeding;
661          else
662            ++tor->secondsDownloading;
663        }
664    }
665
666  /**
667  ***  Set the timer
668  **/
669
670  /* schedule the next timer for right after the next second begins */
671  gettimeofday (&tv, NULL);
672  usec = 1000000 - tv.tv_usec;
673  if (usec > max)
674    usec = max;
675  if (usec < min)
676    usec = min;
677  tr_timerAdd (session->nowTimer, 0, usec);
678  /* fprintf (stderr, "time %zu sec, %zu microsec\n", (size_t)tr_time (), (size_t)tv.tv_usec); */
679}
680
681static void loadBlocklists (tr_session * session);
682
683static void
684tr_sessionInitImpl (void * vdata)
685{
686  tr_variant settings;
687  struct init_data * data = vdata;
688  tr_variant * clientSettings = data->clientSettings;
689  tr_session * session = data->session;
690
691  assert (tr_amInEventThread (session));
692  assert (tr_variantIsDict (clientSettings));
693
694  dbgmsg ("tr_sessionInit: the session's top-level bandwidth object is %p",
695          &session->bandwidth);
696
697  tr_variantInitDict (&settings, 0);
698  tr_sessionGetDefaultSettings (&settings);
699  tr_variantMergeDicts (&settings, clientSettings);
700
701  assert (session->event_base != NULL);
702  session->nowTimer = evtimer_new (session->event_base, onNowTimer, session);
703  onNowTimer (0, 0, session);
704
705#ifndef WIN32
706  /* Don't exit when writing on a broken socket */
707  signal (SIGPIPE, SIG_IGN);
708#endif
709
710  tr_logSetQueueEnabled (data->messageQueuingEnabled);
711
712  tr_setConfigDir (session, data->configDir);
713
714  session->peerMgr = tr_peerMgrNew (session);
715
716  session->shared = tr_sharedInit (session);
717
718  /**
719  ***  Blocklist
720  **/
721
722  {
723    char * filename = tr_buildPath (session->configDir, "blocklists", NULL);
724    tr_mkdirp (filename, 0777);
725    tr_free (filename);
726    loadBlocklists (session);
727  }
728
729  assert (tr_isSession (session));
730
731  session->saveTimer = evtimer_new (session->event_base, onSaveTimer, session);
732  tr_timerAdd (session->saveTimer, SAVE_INTERVAL_SECS, 0);
733
734  tr_announcerInit (session);
735
736  /* first %s is the application name
737     second %s is the version number */
738  tr_logAddInfo (_("%s %s started"), TR_NAME, LONG_VERSION_STRING);
739
740  tr_statsInit (session);
741
742  tr_sessionSet (session, &settings);
743
744  tr_udpInit (session);
745
746  if (session->isLPDEnabled)
747    tr_lpdInit (session, &session->public_ipv4->addr);
748
749  /* cleanup */
750  tr_variantFree (&settings);
751  data->done = true;
752}
753
754static void turtleBootstrap (tr_session *, struct tr_turtle_info *);
755static void setPeerPort (tr_session * session, tr_port port);
756
757static void
758sessionSetImpl (void * vdata)
759{
760  int64_t i;
761  double  d;
762  bool boolVal;
763  const char * str;
764  struct tr_bindinfo b;
765  struct init_data * data = vdata;
766  tr_session * session = data->session;
767  tr_variant * settings = data->clientSettings;
768  struct tr_turtle_info * turtle = &session->turtle;
769
770  assert (tr_isSession (session));
771  assert (tr_variantIsDict (settings));
772  assert (tr_amInEventThread (session));
773
774  if (tr_variantDictFindInt (settings, TR_KEY_message_level, &i))
775    tr_logSetLevel (i);
776
777  if (tr_variantDictFindInt (settings, TR_KEY_umask, &i))
778    {
779      session->umask = (mode_t)i;
780      umask (session->umask);
781    }
782
783  /* misc features */
784  if (tr_variantDictFindInt (settings, TR_KEY_cache_size_mb, &i))
785    tr_sessionSetCacheLimit_MB (session, i);
786  if (tr_variantDictFindInt (settings, TR_KEY_peer_limit_per_torrent, &i))
787    tr_sessionSetPeerLimitPerTorrent (session, i);
788  if (tr_variantDictFindBool (settings, TR_KEY_pex_enabled, &boolVal))
789    tr_sessionSetPexEnabled (session, boolVal);
790  if (tr_variantDictFindBool (settings, TR_KEY_dht_enabled, &boolVal))
791    tr_sessionSetDHTEnabled (session, boolVal);
792  if (tr_variantDictFindBool (settings, TR_KEY_utp_enabled, &boolVal))
793    tr_sessionSetUTPEnabled (session, boolVal);
794  if (tr_variantDictFindBool (settings, TR_KEY_lpd_enabled, &boolVal))
795    tr_sessionSetLPDEnabled (session, boolVal);
796  if (tr_variantDictFindInt (settings, TR_KEY_encryption, &i))
797    tr_sessionSetEncryption (session, i);
798  if (tr_variantDictFindStr (settings, TR_KEY_peer_socket_tos, &str, NULL))
799    session->peerSocketTOS = parse_tos (str);
800  if (tr_variantDictFindStr (settings, TR_KEY_peer_congestion_algorithm, &str, NULL))
801    session->peer_congestion_algorithm = tr_strdup (str);
802  else
803    session->peer_congestion_algorithm = tr_strdup ("");
804  if (tr_variantDictFindBool (settings, TR_KEY_blocklist_enabled, &boolVal))
805    tr_blocklistSetEnabled (session, boolVal);
806  if (tr_variantDictFindStr (settings, TR_KEY_blocklist_url, &str, NULL))
807    tr_blocklistSetURL (session, str);
808  if (tr_variantDictFindBool (settings, TR_KEY_start_added_torrents, &boolVal))
809    tr_sessionSetPaused (session, !boolVal);
810  if (tr_variantDictFindBool (settings, TR_KEY_trash_original_torrent_files, &boolVal))
811    tr_sessionSetDeleteSource (session, boolVal);
812  if (tr_variantDictFindInt (settings, TR_KEY_peer_id_ttl_hours, &i))
813    session->peer_id_ttl_hours = i;
814
815  /* torrent queues */
816  if (tr_variantDictFindInt (settings, TR_KEY_queue_stalled_minutes, &i))
817    tr_sessionSetQueueStalledMinutes (session, i);
818  if (tr_variantDictFindBool (settings, TR_KEY_queue_stalled_enabled, &boolVal))
819    tr_sessionSetQueueStalledEnabled (session, boolVal);
820  if (tr_variantDictFindInt (settings, TR_KEY_download_queue_size, &i))
821    tr_sessionSetQueueSize (session, TR_DOWN, i);
822  if (tr_variantDictFindBool (settings, TR_KEY_download_queue_enabled, &boolVal))
823    tr_sessionSetQueueEnabled (session, TR_DOWN, boolVal);
824  if (tr_variantDictFindInt (settings, TR_KEY_seed_queue_size, &i))
825    tr_sessionSetQueueSize (session, TR_UP, i);
826  if (tr_variantDictFindBool (settings, TR_KEY_seed_queue_enabled, &boolVal))
827    tr_sessionSetQueueEnabled (session, TR_UP, boolVal);
828
829  /* files and directories */
830  if (tr_variantDictFindBool (settings, TR_KEY_prefetch_enabled, &boolVal))
831    session->isPrefetchEnabled = boolVal;
832  if (tr_variantDictFindInt (settings, TR_KEY_preallocation, &i))
833    session->preallocationMode = i;
834  if (tr_variantDictFindStr (settings, TR_KEY_download_dir, &str, NULL))
835    tr_sessionSetDownloadDir (session, str);
836  if (tr_variantDictFindStr (settings, TR_KEY_incomplete_dir, &str, NULL))
837    tr_sessionSetIncompleteDir (session, str);
838  if (tr_variantDictFindBool (settings, TR_KEY_incomplete_dir_enabled, &boolVal))
839    tr_sessionSetIncompleteDirEnabled (session, boolVal);
840  if (tr_variantDictFindBool (settings, TR_KEY_rename_partial_files, &boolVal))
841    tr_sessionSetIncompleteFileNamingEnabled (session, boolVal);
842
843  /* rpc server */
844  if (session->rpcServer != NULL) /* close the old one */
845    tr_rpcClose (&session->rpcServer);
846  session->rpcServer = tr_rpcInit (session, settings);
847
848  /* public addresses */
849
850  free_incoming_peer_port (session);
851
852  tr_variantDictFindStr (settings, TR_KEY_bind_address_ipv4, &str, NULL);
853  if (!tr_address_from_string (&b.addr, str) || (b.addr.type != TR_AF_INET))
854    b.addr = tr_inaddr_any;
855  b.socket = -1;
856  session->public_ipv4 = tr_memdup (&b, sizeof (struct tr_bindinfo));
857
858  tr_variantDictFindStr (settings, TR_KEY_bind_address_ipv6, &str, NULL);
859  if (!tr_address_from_string (&b.addr, str) || (b.addr.type != TR_AF_INET6))
860    b.addr = tr_in6addr_any;
861  b.socket = -1;
862  session->public_ipv6 = tr_memdup (&b, sizeof (struct tr_bindinfo));
863
864  /* incoming peer port */
865  if (tr_variantDictFindInt (settings, TR_KEY_peer_port_random_low, &i))
866    session->randomPortLow = i;
867  if (tr_variantDictFindInt (settings, TR_KEY_peer_port_random_high, &i))
868    session->randomPortHigh = i;
869  if (tr_variantDictFindBool (settings, TR_KEY_peer_port_random_on_start, &boolVal))
870    tr_sessionSetPeerPortRandomOnStart (session, boolVal);
871  if (!tr_variantDictFindInt (settings, TR_KEY_peer_port, &i))
872    i = session->private_peer_port;
873  setPeerPort (session, boolVal ? getRandomPort (session) : i);
874  if (tr_variantDictFindBool (settings, TR_KEY_port_forwarding_enabled, &boolVal))
875    tr_sessionSetPortForwardingEnabled (session, boolVal);
876
877  if (tr_variantDictFindInt (settings, TR_KEY_peer_limit_global, &i))
878    session->peerLimit = i;
879
880  /**
881  **/
882
883  if (tr_variantDictFindInt (settings, TR_KEY_upload_slots_per_torrent, &i))
884    session->uploadSlotsPerTorrent = i;
885
886  if (tr_variantDictFindInt (settings, TR_KEY_speed_limit_up, &i))
887    tr_sessionSetSpeedLimit_KBps (session, TR_UP, i);
888  if (tr_variantDictFindBool (settings, TR_KEY_speed_limit_up_enabled, &boolVal))
889    tr_sessionLimitSpeed (session, TR_UP, boolVal);
890
891  if (tr_variantDictFindInt (settings, TR_KEY_speed_limit_down, &i))
892    tr_sessionSetSpeedLimit_KBps (session, TR_DOWN, i);
893  if (tr_variantDictFindBool (settings, TR_KEY_speed_limit_down_enabled, &boolVal))
894    tr_sessionLimitSpeed (session, TR_DOWN, boolVal);
895
896  if (tr_variantDictFindReal (settings, TR_KEY_ratio_limit, &d))
897    tr_sessionSetRatioLimit (session, d);
898  if (tr_variantDictFindBool (settings, TR_KEY_ratio_limit_enabled, &boolVal))
899    tr_sessionSetRatioLimited (session, boolVal);
900
901  if (tr_variantDictFindInt (settings, TR_KEY_idle_seeding_limit, &i))
902    tr_sessionSetIdleLimit (session, i);
903  if (tr_variantDictFindBool (settings, TR_KEY_idle_seeding_limit_enabled, &boolVal))
904    tr_sessionSetIdleLimited (session, boolVal);
905
906  /**
907  ***  Turtle Mode
908  **/
909
910  /* update the turtle mode's fields */
911  if (tr_variantDictFindInt (settings, TR_KEY_alt_speed_up, &i))
912    turtle->speedLimit_Bps[TR_UP] = toSpeedBytes (i);
913  if (tr_variantDictFindInt (settings, TR_KEY_alt_speed_down, &i))
914    turtle->speedLimit_Bps[TR_DOWN] = toSpeedBytes (i);
915  if (tr_variantDictFindInt (settings, TR_KEY_alt_speed_time_begin, &i))
916    turtle->beginMinute = i;
917  if (tr_variantDictFindInt (settings, TR_KEY_alt_speed_time_end, &i))
918    turtle->endMinute = i;
919  if (tr_variantDictFindInt (settings, TR_KEY_alt_speed_time_day, &i))
920    turtle->days = i;
921  if (tr_variantDictFindBool (settings, TR_KEY_alt_speed_time_enabled, &boolVal))
922    turtle->isClockEnabled = boolVal;
923  if (tr_variantDictFindBool (settings, TR_KEY_alt_speed_enabled, &boolVal))
924    turtle->isEnabled = boolVal;
925  turtleBootstrap (session, turtle);
926
927  /**
928  ***  Scripts
929  **/
930
931  if (tr_variantDictFindBool (settings, TR_KEY_script_torrent_done_enabled, &boolVal))
932    tr_sessionSetTorrentDoneScriptEnabled (session, boolVal);
933  if (tr_variantDictFindStr (settings, TR_KEY_script_torrent_done_filename, &str, NULL))
934    tr_sessionSetTorrentDoneScript (session, str);
935
936
937  if (tr_variantDictFindBool (settings, TR_KEY_scrape_paused_torrents_enabled, &boolVal))
938    session->scrapePausedTorrents = boolVal;
939
940  data->done = true;
941}
942
943void
944tr_sessionSet (tr_session * session, tr_variant * settings)
945{
946  struct init_data data;
947  data.done = false;
948  data.session = session;
949  data.clientSettings = settings;
950
951  /* run the rest in the libtransmission thread */
952  tr_runInEventThread (session, sessionSetImpl, &data);
953  while (!data.done)
954    tr_wait_msec (100);
955}
956
957/***
958****
959***/
960
961void
962tr_sessionSetDownloadDir (tr_session * session, const char * dir)
963{
964  struct tr_device_info * info = NULL;
965
966  assert (tr_isSession (session));
967
968  if (dir != NULL)
969    info = tr_device_info_create (dir);
970  tr_device_info_free (session->downloadDir);
971  session->downloadDir = info;
972}
973
974const char *
975tr_sessionGetDownloadDir (const tr_session * session)
976{
977  const char * dir = NULL;
978
979  assert (tr_isSession (session));
980
981  if ((session != NULL) && (session->downloadDir != NULL))
982    dir = session->downloadDir->path;
983
984  return dir;
985}
986
987int64_t
988tr_sessionGetDirFreeSpace (tr_session * session, const char * dir)
989{
990  int64_t free_space;
991
992  if (!tr_strcmp0 (dir, tr_sessionGetDownloadDir (session)))
993    free_space = tr_device_info_get_free_space (session->downloadDir);
994  else
995    free_space = tr_getDirFreeSpace (dir);
996
997  return free_space;
998}
999
1000/***
1001****
1002***/
1003
1004void
1005tr_sessionSetIncompleteFileNamingEnabled (tr_session * session, bool b)
1006{
1007  assert (tr_isSession (session));
1008  assert (tr_isBool (b));
1009
1010  session->isIncompleteFileNamingEnabled = b;
1011}
1012
1013bool
1014tr_sessionIsIncompleteFileNamingEnabled (const tr_session * session)
1015{
1016  assert (tr_isSession (session));
1017
1018  return session->isIncompleteFileNamingEnabled;
1019}
1020
1021/***
1022****
1023***/
1024
1025
1026void
1027tr_sessionSetIncompleteDir (tr_session * session, const char * dir)
1028{
1029  assert (tr_isSession (session));
1030
1031  if (session->incompleteDir != dir)
1032    {
1033      tr_free (session->incompleteDir);
1034
1035      session->incompleteDir = tr_strdup (dir);
1036    }
1037}
1038
1039const char*
1040tr_sessionGetIncompleteDir (const tr_session * session)
1041{
1042  assert (tr_isSession (session));
1043
1044  return session->incompleteDir;
1045}
1046
1047void
1048tr_sessionSetIncompleteDirEnabled (tr_session * session, bool b)
1049{
1050  assert (tr_isSession (session));
1051  assert (tr_isBool (b));
1052
1053  session->isIncompleteDirEnabled = b;
1054}
1055
1056bool
1057tr_sessionIsIncompleteDirEnabled (const tr_session * session)
1058{
1059  assert (tr_isSession (session));
1060
1061  return session->isIncompleteDirEnabled;
1062}
1063
1064/***
1065****
1066***/
1067
1068void
1069tr_sessionLock (tr_session * session)
1070{
1071  assert (tr_isSession (session));
1072
1073  tr_lockLock (session->lock);
1074}
1075
1076void
1077tr_sessionUnlock (tr_session * session)
1078{
1079  assert (tr_isSession (session));
1080
1081  tr_lockUnlock (session->lock);
1082}
1083
1084bool
1085tr_sessionIsLocked (const tr_session * session)
1086{
1087  return tr_isSession (session) && tr_lockHave (session->lock);
1088}
1089
1090/***
1091****  Peer Port
1092***/
1093
1094static void
1095peerPortChanged (void * session)
1096{
1097  tr_torrent * tor = NULL;
1098
1099  assert (tr_isSession (session));
1100
1101  close_incoming_peer_port (session);
1102  open_incoming_peer_port (session);
1103  tr_sharedPortChanged (session);
1104
1105  while ((tor = tr_torrentNext (session, tor)))
1106    tr_torrentChangeMyPort (tor);
1107}
1108
1109static void
1110setPeerPort (tr_session * session, tr_port port)
1111{
1112  session->private_peer_port = port;
1113  session->public_peer_port = port;
1114
1115  tr_runInEventThread (session, peerPortChanged, session);
1116}
1117
1118void
1119tr_sessionSetPeerPort (tr_session * session, tr_port port)
1120{
1121  if (tr_isSession (session) && (session->private_peer_port != port))
1122    {
1123      setPeerPort (session, port);
1124    }
1125}
1126
1127tr_port
1128tr_sessionGetPeerPort (const tr_session * session)
1129{
1130  return tr_isSession (session) ? session->private_peer_port : 0;
1131}
1132
1133tr_port
1134tr_sessionSetPeerPortRandom (tr_session * session)
1135{
1136  assert (tr_isSession (session));
1137
1138  tr_sessionSetPeerPort (session, getRandomPort (session));
1139  return session->private_peer_port;
1140}
1141
1142void
1143tr_sessionSetPeerPortRandomOnStart (tr_session * session,
1144                                    bool random)
1145{
1146  assert (tr_isSession (session));
1147
1148  session->isPortRandom = random;
1149}
1150
1151bool
1152tr_sessionGetPeerPortRandomOnStart (tr_session * session)
1153{
1154  assert (tr_isSession (session));
1155
1156  return session->isPortRandom;
1157}
1158
1159tr_port_forwarding
1160tr_sessionGetPortForwarding (const tr_session * session)
1161{
1162  assert (tr_isSession (session));
1163
1164  return tr_sharedTraversalStatus (session->shared);
1165}
1166
1167/***
1168****
1169***/
1170
1171void
1172tr_sessionSetRatioLimited (tr_session * session, bool isLimited)
1173{
1174  assert (tr_isSession (session));
1175
1176  session->isRatioLimited = isLimited;
1177}
1178
1179void
1180tr_sessionSetRatioLimit (tr_session * session, double desiredRatio)
1181{
1182  assert (tr_isSession (session));
1183
1184  session->desiredRatio = desiredRatio;
1185}
1186
1187bool
1188tr_sessionIsRatioLimited (const tr_session  * session)
1189{
1190  assert (tr_isSession (session));
1191
1192  return session->isRatioLimited;
1193}
1194
1195double
1196tr_sessionGetRatioLimit (const tr_session * session)
1197{
1198  assert (tr_isSession (session));
1199
1200  return session->desiredRatio;
1201}
1202
1203/***
1204****
1205***/
1206
1207void
1208tr_sessionSetIdleLimited (tr_session * session, bool isLimited)
1209{
1210  assert (tr_isSession (session));
1211
1212  session->isIdleLimited = isLimited;
1213}
1214
1215void
1216tr_sessionSetIdleLimit (tr_session * session, uint16_t idleMinutes)
1217{
1218  assert (tr_isSession (session));
1219
1220  session->idleLimitMinutes = idleMinutes;
1221}
1222
1223bool
1224tr_sessionIsIdleLimited (const tr_session  * session)
1225{
1226  assert (tr_isSession (session));
1227
1228  return session->isIdleLimited;
1229}
1230
1231uint16_t
1232tr_sessionGetIdleLimit (const tr_session * session)
1233{
1234  assert (tr_isSession (session));
1235
1236  return session->idleLimitMinutes;
1237}
1238
1239/***
1240****
1241****  SPEED LIMITS
1242****
1243***/
1244
1245bool
1246tr_sessionGetActiveSpeedLimit_Bps (const tr_session * session, tr_direction dir, unsigned int * setme_Bps)
1247{
1248  int isLimited = true;
1249
1250  if (!tr_isSession (session))
1251    return false;
1252
1253  if (tr_sessionUsesAltSpeed (session))
1254    *setme_Bps = tr_sessionGetAltSpeed_Bps (session, dir);
1255  else if (tr_sessionIsSpeedLimited (session, dir))
1256    *setme_Bps = tr_sessionGetSpeedLimit_Bps (session, dir);
1257  else
1258    isLimited = false;
1259
1260  return isLimited;
1261}
1262bool
1263tr_sessionGetActiveSpeedLimit_KBps (const tr_session  * session,
1264                                    tr_direction        dir,
1265                                    double            * setme_KBps)
1266{
1267  unsigned int Bps = 0;
1268  const bool is_active = tr_sessionGetActiveSpeedLimit_Bps (session, dir, &Bps);
1269  *setme_KBps = toSpeedKBps (Bps);
1270  return is_active;
1271}
1272
1273static void
1274updateBandwidth (tr_session * session, tr_direction dir)
1275{
1276  unsigned int limit_Bps = 0;
1277  const bool isLimited = tr_sessionGetActiveSpeedLimit_Bps (session, dir, &limit_Bps);
1278  const bool zeroCase = isLimited && !limit_Bps;
1279
1280  tr_bandwidthSetLimited (&session->bandwidth, dir, isLimited && !zeroCase);
1281
1282  tr_bandwidthSetDesiredSpeed_Bps (&session->bandwidth, dir, limit_Bps);
1283}
1284
1285enum
1286{
1287  MINUTES_PER_HOUR = 60,
1288  MINUTES_PER_DAY = MINUTES_PER_HOUR * 24,
1289  MINUTES_PER_WEEK = MINUTES_PER_DAY * 7
1290};
1291
1292static void
1293turtleUpdateTable (struct tr_turtle_info * t)
1294{
1295  int day;
1296  tr_bitfield * b = &t->minutes;
1297
1298  tr_bitfieldSetHasNone (b);
1299
1300  for (day=0; day<7; ++day)
1301    {
1302      if (t->days & (1<<day))
1303        {
1304          int i;
1305          const time_t begin = t->beginMinute;
1306          time_t end = t->endMinute;
1307
1308          if (end <= begin)
1309            end += MINUTES_PER_DAY;
1310
1311          for (i=begin; i<end; ++i)
1312            tr_bitfieldAdd (b, (i+day*MINUTES_PER_DAY) % MINUTES_PER_WEEK);
1313        }
1314    }
1315}
1316
1317static void
1318altSpeedToggled (void * vsession)
1319{
1320  tr_session * session = vsession;
1321  struct tr_turtle_info * t = &session->turtle;
1322
1323  assert (tr_isSession (session));
1324
1325  updateBandwidth (session, TR_UP);
1326  updateBandwidth (session, TR_DOWN);
1327
1328  if (t->callback != NULL)
1329    (*t->callback)(session, t->isEnabled, t->changedByUser, t->callbackUserData);
1330}
1331
1332static void
1333useAltSpeed (tr_session * s, struct tr_turtle_info * t,
1334             bool enabled, bool byUser)
1335{
1336  assert (tr_isSession (s));
1337  assert (t != NULL);
1338  assert (tr_isBool (enabled));
1339  assert (tr_isBool (byUser));
1340
1341  if (t->isEnabled != enabled)
1342    {
1343      t->isEnabled = enabled;
1344      t->changedByUser = byUser;
1345      tr_runInEventThread (s, altSpeedToggled, s);
1346    }
1347}
1348
1349/**
1350 * @return whether turtle should be on/off according to the scheduler
1351 */
1352static bool
1353getInTurtleTime (const struct tr_turtle_info * t)
1354{
1355  struct tm tm;
1356  size_t minute_of_the_week;
1357  const time_t now = tr_time ();
1358
1359  tr_localtime_r (&now, &tm);
1360
1361  minute_of_the_week = tm.tm_wday * MINUTES_PER_DAY
1362                     + tm.tm_hour * MINUTES_PER_HOUR
1363                     + tm.tm_min;
1364  if (minute_of_the_week >= MINUTES_PER_WEEK) /* leap minutes? */
1365    minute_of_the_week = MINUTES_PER_WEEK - 1;
1366
1367  return tr_bitfieldHas (&t->minutes, minute_of_the_week);
1368}
1369
1370static inline tr_auto_switch_state_t
1371autoSwitchState (bool enabled)
1372{
1373    return enabled ? TR_AUTO_SWITCH_ON : TR_AUTO_SWITCH_OFF;
1374}
1375
1376static void
1377turtleCheckClock (tr_session * s, struct tr_turtle_info * t)
1378{
1379  bool enabled;
1380  bool alreadySwitched;
1381  tr_auto_switch_state_t newAutoTurtleState;
1382
1383  assert (t->isClockEnabled);
1384
1385  enabled = getInTurtleTime (t);
1386  newAutoTurtleState = autoSwitchState (enabled);
1387  alreadySwitched = (t->autoTurtleState == newAutoTurtleState);
1388
1389  if (!alreadySwitched)
1390    {
1391      tr_logAddInfo ("Time to turn %s turtle mode!", (enabled?"on":"off"));
1392      t->autoTurtleState = newAutoTurtleState;
1393      useAltSpeed (s, t, enabled, false);
1394    }
1395}
1396
1397/* Called after the turtle's fields are loaded from an outside source.
1398 * It initializes the implementation fields
1399 * and turns on turtle mode if the clock settings say to. */
1400static void
1401turtleBootstrap (tr_session * session, struct tr_turtle_info * turtle)
1402{
1403  turtle->changedByUser = false;
1404  turtle->autoTurtleState = TR_AUTO_SWITCH_UNUSED;
1405
1406  tr_bitfieldConstruct (&turtle->minutes, MINUTES_PER_WEEK);
1407
1408  turtleUpdateTable (turtle);
1409
1410  if (turtle->isClockEnabled)
1411    {
1412      turtle->isEnabled = getInTurtleTime (turtle);
1413      turtle->autoTurtleState = autoSwitchState (turtle->isEnabled);
1414    }
1415
1416  altSpeedToggled (session);
1417
1418}
1419
1420/***
1421****  Primary session speed limits
1422***/
1423
1424void
1425tr_sessionSetSpeedLimit_Bps (tr_session * s, tr_direction d, unsigned int Bps)
1426{
1427  assert (tr_isSession (s));
1428  assert (tr_isDirection (d));
1429
1430  s->speedLimit_Bps[d] = Bps;
1431
1432  updateBandwidth (s, d);
1433}
1434void
1435tr_sessionSetSpeedLimit_KBps (tr_session * s, tr_direction d, unsigned int KBps)
1436{
1437  tr_sessionSetSpeedLimit_Bps (s, d, toSpeedBytes (KBps));
1438}
1439
1440unsigned int
1441tr_sessionGetSpeedLimit_Bps (const tr_session * s, tr_direction d)
1442{
1443  assert (tr_isSession (s));
1444  assert (tr_isDirection (d));
1445
1446  return s->speedLimit_Bps[d];
1447}
1448unsigned int
1449tr_sessionGetSpeedLimit_KBps (const tr_session * s, tr_direction d)
1450{
1451    return toSpeedKBps (tr_sessionGetSpeedLimit_Bps (s, d));
1452}
1453
1454void
1455tr_sessionLimitSpeed (tr_session * s, tr_direction d, bool b)
1456{
1457  assert (tr_isSession (s));
1458  assert (tr_isDirection (d));
1459  assert (tr_isBool (b));
1460
1461  s->speedLimitEnabled[d] = b;
1462
1463  updateBandwidth (s, d);
1464}
1465
1466bool
1467tr_sessionIsSpeedLimited (const tr_session * s, tr_direction d)
1468{
1469  assert (tr_isSession (s));
1470  assert (tr_isDirection (d));
1471
1472  return s->speedLimitEnabled[d];
1473}
1474
1475/***
1476****  Alternative speed limits that are used during scheduled times
1477***/
1478
1479void
1480tr_sessionSetAltSpeed_Bps (tr_session * s, tr_direction d, unsigned int Bps)
1481{
1482  assert (tr_isSession (s));
1483  assert (tr_isDirection (d));
1484
1485  s->turtle.speedLimit_Bps[d] = Bps;
1486
1487  updateBandwidth (s, d);
1488}
1489
1490void
1491tr_sessionSetAltSpeed_KBps (tr_session * s, tr_direction d, unsigned int KBps)
1492{
1493  tr_sessionSetAltSpeed_Bps (s, d, toSpeedBytes (KBps));
1494}
1495
1496unsigned int
1497tr_sessionGetAltSpeed_Bps (const tr_session * s, tr_direction d)
1498{
1499  assert (tr_isSession (s));
1500  assert (tr_isDirection (d));
1501
1502  return s->turtle.speedLimit_Bps[d];
1503}
1504unsigned int
1505tr_sessionGetAltSpeed_KBps (const tr_session * s, tr_direction d)
1506{
1507  return toSpeedKBps (tr_sessionGetAltSpeed_Bps (s, d));
1508}
1509
1510static void
1511userPokedTheClock (tr_session * s, struct tr_turtle_info * t)
1512{
1513  tr_logAddDebug ("Refreshing the turtle mode clock due to user changes");
1514
1515  t->autoTurtleState = TR_AUTO_SWITCH_UNUSED;
1516
1517  turtleUpdateTable (t);
1518
1519  if (t->isClockEnabled)
1520    {
1521      const bool enabled = getInTurtleTime (t);
1522      useAltSpeed (s, t, enabled, true);
1523      t->autoTurtleState = autoSwitchState (enabled);
1524    }
1525}
1526
1527void
1528tr_sessionUseAltSpeedTime (tr_session * s, bool b)
1529{
1530  struct tr_turtle_info * t = &s->turtle;
1531
1532  assert (tr_isSession (s));
1533  assert (tr_isBool (b));
1534
1535  if (t->isClockEnabled != b)
1536    {
1537      t->isClockEnabled = b;
1538      userPokedTheClock (s, t);
1539    }
1540}
1541
1542bool
1543tr_sessionUsesAltSpeedTime (const tr_session * s)
1544{
1545  assert (tr_isSession (s));
1546
1547  return s->turtle.isClockEnabled;
1548}
1549
1550void
1551tr_sessionSetAltSpeedBegin (tr_session * s, int minute)
1552{
1553  assert (tr_isSession (s));
1554  assert (0<=minute && minute< (60*24));
1555
1556  if (s->turtle.beginMinute != minute)
1557    {
1558      s->turtle.beginMinute = minute;
1559      userPokedTheClock (s, &s->turtle);
1560    }
1561}
1562
1563int
1564tr_sessionGetAltSpeedBegin (const tr_session * s)
1565{
1566  assert (tr_isSession (s));
1567
1568  return s->turtle.beginMinute;
1569}
1570
1571void
1572tr_sessionSetAltSpeedEnd (tr_session * s, int minute)
1573{
1574  assert (tr_isSession (s));
1575  assert (0<=minute && minute< (60*24));
1576
1577  if (s->turtle.endMinute != minute)
1578    {
1579      s->turtle.endMinute = minute;
1580      userPokedTheClock (s, &s->turtle);
1581    }
1582}
1583
1584int
1585tr_sessionGetAltSpeedEnd (const tr_session * s)
1586{
1587  assert (tr_isSession (s));
1588
1589  return s->turtle.endMinute;
1590}
1591
1592void
1593tr_sessionSetAltSpeedDay (tr_session * s, tr_sched_day days)
1594{
1595  assert (tr_isSession (s));
1596
1597  if (s->turtle.days != days)
1598    {
1599      s->turtle.days = days;
1600      userPokedTheClock (s, &s->turtle);
1601    }
1602}
1603
1604tr_sched_day
1605tr_sessionGetAltSpeedDay (const tr_session * s)
1606{
1607  assert (tr_isSession (s));
1608
1609  return s->turtle.days;
1610}
1611
1612void
1613tr_sessionUseAltSpeed (tr_session * session, bool enabled)
1614{
1615  useAltSpeed (session, &session->turtle, enabled, true);
1616}
1617
1618bool
1619tr_sessionUsesAltSpeed (const tr_session * s)
1620{
1621  assert (tr_isSession (s));
1622
1623  return s->turtle.isEnabled;
1624}
1625
1626void
1627tr_sessionSetAltSpeedFunc (tr_session       * session,
1628                           tr_altSpeedFunc    func,
1629                           void             * userData)
1630{
1631  assert (tr_isSession (session));
1632
1633  session->turtle.callback = func;
1634  session->turtle.callbackUserData = userData;
1635}
1636
1637void
1638tr_sessionClearAltSpeedFunc (tr_session * session)
1639{
1640  tr_sessionSetAltSpeedFunc (session, NULL, NULL);
1641}
1642
1643/***
1644****
1645***/
1646
1647void
1648tr_sessionSetPeerLimit (tr_session * session, uint16_t n)
1649{
1650  assert (tr_isSession (session));
1651
1652  session->peerLimit = n;
1653}
1654
1655uint16_t
1656tr_sessionGetPeerLimit (const tr_session * session)
1657{
1658  assert (tr_isSession (session));
1659
1660  return session->peerLimit;
1661}
1662
1663void
1664tr_sessionSetPeerLimitPerTorrent (tr_session  * session, uint16_t n)
1665{
1666    assert (tr_isSession (session));
1667
1668    session->peerLimitPerTorrent = n;
1669}
1670
1671uint16_t
1672tr_sessionGetPeerLimitPerTorrent (const tr_session * session)
1673{
1674  assert (tr_isSession (session));
1675
1676  return session->peerLimitPerTorrent;
1677}
1678
1679/***
1680****
1681***/
1682
1683void
1684tr_sessionSetPaused (tr_session * session, bool isPaused)
1685{
1686  assert (tr_isSession (session));
1687
1688  session->pauseAddedTorrent = isPaused;
1689}
1690
1691bool
1692tr_sessionGetPaused (const tr_session * session)
1693{
1694  assert (tr_isSession (session));
1695
1696  return session->pauseAddedTorrent;
1697}
1698
1699void
1700tr_sessionSetDeleteSource (tr_session * session, bool deleteSource)
1701{
1702  assert (tr_isSession (session));
1703
1704  session->deleteSourceTorrent = deleteSource;
1705}
1706
1707bool
1708tr_sessionGetDeleteSource (const tr_session * session)
1709{
1710  assert (tr_isSession (session));
1711
1712  return session->deleteSourceTorrent;
1713}
1714
1715/***
1716****
1717***/
1718
1719unsigned int
1720tr_sessionGetPieceSpeed_Bps (const tr_session * session, tr_direction dir)
1721{
1722  return tr_isSession (session) ? tr_bandwidthGetPieceSpeed_Bps (&session->bandwidth, 0, dir) : 0;
1723}
1724
1725unsigned int
1726tr_sessionGetRawSpeed_Bps (const tr_session * session, tr_direction dir)
1727{
1728  return tr_isSession (session) ? tr_bandwidthGetRawSpeed_Bps (&session->bandwidth, 0, dir) : 0;
1729}
1730double
1731tr_sessionGetRawSpeed_KBps (const tr_session * session, tr_direction dir)
1732{
1733  return toSpeedKBps (tr_sessionGetRawSpeed_Bps (session, dir));
1734}
1735
1736
1737int
1738tr_sessionCountTorrents (const tr_session * session)
1739{
1740  return tr_isSession (session) ? session->torrentCount : 0;
1741}
1742
1743tr_torrent **
1744tr_sessionGetTorrents (tr_session * session, int * setme_n)
1745{
1746  int i;
1747  int n;
1748  tr_torrent ** torrents;
1749  tr_torrent * tor;
1750
1751  assert (tr_isSession (session));
1752  assert (setme_n != NULL);
1753
1754  n = tr_sessionCountTorrents (session);
1755  *setme_n = n;
1756
1757  torrents = tr_new (tr_torrent *, n);
1758  tor = NULL;
1759  for (i=0; i<n; ++i)
1760    torrents[i] = tor = tr_torrentNext (session, tor);
1761
1762  return torrents;
1763} 
1764
1765static int
1766compareTorrentByCur (const void * va, const void * vb)
1767{
1768  const tr_torrent * a = * (const tr_torrent**)va;
1769  const tr_torrent * b = * (const tr_torrent**)vb;
1770  const uint64_t aCur = a->downloadedCur + a->uploadedCur;
1771  const uint64_t bCur = b->downloadedCur + b->uploadedCur;
1772
1773  if (aCur != bCur)
1774    return aCur > bCur ? -1 : 1; /* close the biggest torrents first */
1775
1776  return 0;
1777}
1778
1779static void closeBlocklists (tr_session *);
1780
1781static void
1782sessionCloseImpl (void * vsession)
1783{
1784  int i, n;
1785  tr_torrent ** torrents;
1786  tr_session * session = vsession;
1787
1788  assert (tr_isSession (session));
1789
1790  session->isClosing = true;
1791
1792  free_incoming_peer_port (session);
1793
1794  if (session->isLPDEnabled)
1795    tr_lpdUninit (session);
1796
1797  tr_utpClose (session);
1798  tr_dhtUninit (session);
1799
1800  event_free (session->saveTimer);
1801  session->saveTimer = NULL;
1802
1803  event_free (session->nowTimer);
1804  session->nowTimer = NULL;
1805
1806  tr_verifyClose (session);
1807  tr_sharedClose (session);
1808  tr_rpcClose (&session->rpcServer);
1809
1810  /* Close the torrents. Get the most active ones first so that
1811   * if we can't get them all closed in a reasonable amount of time,
1812   * at least we get the most important ones first. */
1813  torrents = tr_sessionGetTorrents (session, &n);
1814  qsort (torrents, n, sizeof (tr_torrent*), compareTorrentByCur);
1815  for (i=0; i<n; ++i)
1816    tr_torrentFree (torrents[i]);
1817  tr_free (torrents);
1818
1819  /* Close the announcer *after* closing the torrents
1820     so that all the &event=stopped messages will be
1821     queued to be sent by tr_announcerClose () */
1822  tr_announcerClose (session);
1823
1824  /* and this goes *after* announcer close so that
1825     it won't be idle until the announce events are sent... */
1826  tr_webClose (session, TR_WEB_CLOSE_WHEN_IDLE);
1827
1828  tr_cacheFree (session->cache);
1829  session->cache = NULL;
1830
1831  /* gotta keep udp running long enough to send out all
1832     the &event=stopped UDP tracker messages */
1833  while (!tr_tracker_udp_is_idle (session))
1834    {
1835      tr_tracker_udp_upkeep (session);
1836      tr_wait_msec (100);
1837    }
1838
1839  /* we had to wait until UDP trackers were closed before closing these: */
1840  evdns_base_free (session->evdns_base, 0);
1841  session->evdns_base = NULL;
1842  tr_tracker_udp_close (session);
1843  tr_udpUninit (session);
1844
1845  tr_statsClose (session);
1846  tr_peerMgrFree (session->peerMgr);
1847
1848  closeBlocklists (session);
1849
1850  tr_fdClose (session);
1851
1852  session->isClosed = true;
1853}
1854
1855static int
1856deadlineReached (const time_t deadline)
1857{
1858  return time (NULL) >= deadline;
1859}
1860
1861#define SHUTDOWN_MAX_SECONDS 20
1862
1863void
1864tr_sessionClose (tr_session * session)
1865{
1866  const time_t deadline = time (NULL) + SHUTDOWN_MAX_SECONDS;
1867
1868  assert (tr_isSession (session));
1869
1870  dbgmsg ("shutting down transmission session %p... now is %zu, deadline is %zu", session, (size_t)time (NULL), (size_t)deadline);
1871
1872  /* close the session */
1873  tr_runInEventThread (session, sessionCloseImpl, session);
1874  while (!session->isClosed && !deadlineReached (deadline))
1875    {
1876      dbgmsg ("waiting for the libtransmission thread to finish");
1877      tr_wait_msec (100);
1878    }
1879
1880  /* "shared" and "tracker" have live sockets,
1881   * so we need to keep the transmission thread alive
1882   * for a bit while they tell the router & tracker
1883   * that we're closing now */
1884  while ((session->shared || session->web || session->announcer || session->announcer_udp)
1885           && !deadlineReached (deadline))
1886    {
1887      dbgmsg ("waiting on port unmap (%p) or announcer (%p)... now %zu deadline %zu",
1888              session->shared, session->announcer, (size_t)time (NULL), (size_t)deadline);
1889      tr_wait_msec (50);
1890    }
1891
1892  tr_webClose (session, TR_WEB_CLOSE_NOW);
1893
1894  /* close the libtransmission thread */
1895  tr_eventClose (session);
1896  while (session->events != NULL)
1897    {
1898      static bool forced = false;
1899      dbgmsg ("waiting for libtransmission thread to finish... now %zu deadline %zu", (size_t)time (NULL), (size_t)deadline);
1900      tr_wait_msec (100);
1901
1902      if (deadlineReached (deadline) && !forced)
1903        {
1904          dbgmsg ("calling event_loopbreak ()");
1905          forced = true;
1906          event_base_loopbreak (session->event_base);
1907        }
1908
1909      if (deadlineReached (deadline+3))
1910        {
1911          dbgmsg ("deadline+3 reached... calling break...\n");
1912          break;
1913        }
1914    }
1915
1916  /* free the session memory */
1917  tr_variantFree (&session->removedTorrents);
1918  tr_bandwidthDestruct (&session->bandwidth);
1919  tr_bitfieldDestruct (&session->turtle.minutes);
1920  tr_lockFree (session->lock);
1921  if (session->metainfoLookup)
1922    {
1923      tr_variantFree (session->metainfoLookup);
1924      tr_free (session->metainfoLookup);
1925    }
1926  tr_device_info_free (session->downloadDir);
1927  tr_free (session->torrentDoneScript);
1928  tr_free (session->tag);
1929  tr_free (session->configDir);
1930  tr_free (session->resumeDir);
1931  tr_free (session->torrentDir);
1932  tr_free (session->incompleteDir);
1933  tr_free (session->blocklist_url);
1934  tr_free (session->peer_congestion_algorithm);
1935  tr_free (session);
1936}
1937
1938struct sessionLoadTorrentsData
1939{
1940  tr_session * session;
1941  tr_ctor * ctor;
1942  int * setmeCount;
1943  tr_torrent ** torrents;
1944  bool done;
1945};
1946
1947static void
1948sessionLoadTorrents (void * vdata)
1949{
1950  int i;
1951  int n = 0;
1952  struct stat sb;
1953  DIR * odir = NULL;
1954  tr_list * l = NULL;
1955  tr_list * list = NULL;
1956  struct sessionLoadTorrentsData * data = vdata;
1957  const char * dirname = tr_getTorrentDir (data->session);
1958
1959  assert (tr_isSession (data->session));
1960
1961  tr_ctorSetSave (data->ctor, false); /* since we already have them */
1962
1963  if (!stat (dirname, &sb)
1964      && S_ISDIR (sb.st_mode)
1965      && ((odir = opendir (dirname))))
1966    {
1967      struct dirent *d;
1968      for (d = readdir (odir); d != NULL; d = readdir (odir))
1969        {
1970          if (tr_str_has_suffix (d->d_name, ".torrent"))
1971            {
1972              tr_torrent * tor;
1973              char * path = tr_buildPath (dirname, d->d_name, NULL);
1974              tr_ctorSetMetainfoFromFile (data->ctor, path);
1975              if ((tor = tr_torrentNew (data->ctor, NULL, NULL)))
1976                {
1977                  tr_list_prepend (&list, tor);
1978                  ++n;
1979                }
1980              tr_free (path);
1981            }
1982        }
1983      closedir (odir);
1984    }
1985
1986  data->torrents = tr_new (tr_torrent *, n);
1987  for (i=0, l=list; l!=NULL; l=l->next)
1988    data->torrents[i++] = (tr_torrent*) l->data;
1989  assert (i == n);
1990
1991    tr_list_free (&list, NULL);
1992
1993  if (n)
1994    tr_logAddInfo (_("Loaded %d torrents"), n);
1995
1996  if (data->setmeCount)
1997    *data->setmeCount = n;
1998
1999  data->done = true;
2000}
2001
2002tr_torrent **
2003tr_sessionLoadTorrents (tr_session * session,
2004                        tr_ctor    * ctor,
2005                        int        * setmeCount)
2006{
2007  struct sessionLoadTorrentsData data;
2008
2009  data.session = session;
2010  data.ctor = ctor;
2011  data.setmeCount = setmeCount;
2012  data.torrents = NULL;
2013  data.done = false;
2014
2015  tr_runInEventThread (session, sessionLoadTorrents, &data);
2016  while (!data.done)
2017    tr_wait_msec (100);
2018
2019  return data.torrents;
2020}
2021
2022/***
2023****
2024***/
2025
2026void
2027tr_sessionSetPexEnabled (tr_session * session, bool enabled)
2028{
2029  assert (tr_isSession (session));
2030
2031  session->isPexEnabled = enabled != 0;
2032}
2033
2034bool
2035tr_sessionIsPexEnabled (const tr_session * session)
2036{
2037  assert (tr_isSession (session));
2038
2039  return session->isPexEnabled;
2040}
2041
2042bool
2043tr_sessionAllowsDHT (const tr_session * session)
2044{
2045  return tr_sessionIsDHTEnabled (session);
2046}
2047
2048bool
2049tr_sessionIsDHTEnabled (const tr_session * session)
2050{
2051  assert (tr_isSession (session));
2052
2053  return session->isDHTEnabled;
2054}
2055
2056static void
2057toggleDHTImpl (void * data)
2058{
2059  tr_session * session = data;
2060  assert (tr_isSession (session));
2061
2062  tr_udpUninit (session);
2063  session->isDHTEnabled = !session->isDHTEnabled;
2064  tr_udpInit (session);
2065}
2066
2067void
2068tr_sessionSetDHTEnabled (tr_session * session, bool enabled)
2069{
2070  assert (tr_isSession (session));
2071  assert (tr_isBool (enabled));
2072
2073  if ((enabled != 0) != (session->isDHTEnabled != 0))
2074    tr_runInEventThread (session, toggleDHTImpl, session);
2075}
2076
2077/***
2078****
2079***/
2080
2081bool
2082tr_sessionIsUTPEnabled (const tr_session * session)
2083{
2084  assert (tr_isSession (session));
2085
2086#ifdef WITH_UTP
2087  return session->isUTPEnabled;
2088#else
2089  return false;
2090#endif
2091}
2092
2093static void
2094toggle_utp (void * data)
2095{
2096  tr_session * session = data;
2097
2098  assert (tr_isSession (session));
2099
2100  session->isUTPEnabled = !session->isUTPEnabled;
2101
2102  tr_udpSetSocketBuffers (session);
2103
2104  /* But don't call tr_utpClose -- see reset_timer in tr-utp.c for an
2105     explanation. */
2106}
2107
2108void
2109tr_sessionSetUTPEnabled (tr_session * session, bool enabled)
2110{
2111  assert (tr_isSession (session));
2112  assert (tr_isBool (enabled));
2113
2114  if ((enabled != 0) != (session->isUTPEnabled != 0))
2115    tr_runInEventThread (session, toggle_utp, session);
2116}
2117
2118/***
2119****
2120***/
2121
2122static void
2123toggleLPDImpl (void * data)
2124{
2125  tr_session * session = data;
2126  assert (tr_isSession (session));
2127
2128  if (session->isLPDEnabled)
2129    tr_lpdUninit (session);
2130
2131  session->isLPDEnabled = !session->isLPDEnabled;
2132
2133  if (session->isLPDEnabled)
2134    tr_lpdInit (session, &session->public_ipv4->addr);
2135}
2136
2137void
2138tr_sessionSetLPDEnabled (tr_session * session, bool enabled)
2139{
2140  assert (tr_isSession (session));
2141  assert (tr_isBool (enabled));
2142
2143  if ((enabled != 0) != (session->isLPDEnabled != 0))
2144    tr_runInEventThread (session, toggleLPDImpl, session);
2145}
2146
2147bool
2148tr_sessionIsLPDEnabled (const tr_session * session)
2149{
2150  assert (tr_isSession (session));
2151
2152  return session->isLPDEnabled;
2153}
2154
2155bool
2156tr_sessionAllowsLPD (const tr_session * session)
2157{
2158  return tr_sessionIsLPDEnabled (session);
2159}
2160
2161/***
2162****
2163***/
2164
2165void
2166tr_sessionSetCacheLimit_MB (tr_session * session, int max_bytes)
2167{
2168  assert (tr_isSession (session));
2169
2170  tr_cacheSetLimit (session->cache, toMemBytes (max_bytes));
2171}
2172
2173int
2174tr_sessionGetCacheLimit_MB (const tr_session * session)
2175{
2176  assert (tr_isSession (session));
2177
2178  return toMemMB (tr_cacheGetLimit (session->cache));
2179}
2180
2181/***
2182****
2183***/
2184
2185struct port_forwarding_data
2186{
2187  bool enabled;
2188  struct tr_shared * shared;
2189};
2190
2191static void
2192setPortForwardingEnabled (void * vdata)
2193{
2194  struct port_forwarding_data * data = vdata;
2195  tr_sharedTraversalEnable (data->shared, data->enabled);
2196  tr_free (data);
2197}
2198
2199void
2200tr_sessionSetPortForwardingEnabled (tr_session  * session, bool enabled)
2201{
2202  struct port_forwarding_data * d;
2203  d = tr_new0 (struct port_forwarding_data, 1);
2204  d->shared = session->shared;
2205  d->enabled = enabled;
2206  tr_runInEventThread (session, setPortForwardingEnabled, d);
2207}
2208
2209bool
2210tr_sessionIsPortForwardingEnabled (const tr_session * session)
2211{
2212  assert (tr_isSession (session));
2213
2214  return tr_sharedTraversalIsEnabled (session->shared);
2215}
2216
2217/***
2218****
2219***/
2220
2221static int
2222tr_stringEndsWith (const char * str, const char * end)
2223{
2224  const size_t slen = strlen (str);
2225  const size_t elen = strlen (end);
2226
2227  return slen >= elen && !memcmp (&str[slen - elen], end, elen);
2228}
2229
2230static void
2231loadBlocklists (tr_session * session)
2232{
2233  DIR * odir;
2234  char * dirname;
2235  struct dirent * d;
2236  tr_list * blocklists = NULL;
2237  tr_ptrArray loadme = TR_PTR_ARRAY_INIT;
2238  const bool isEnabled = session->isBlocklistEnabled;
2239
2240  /* walk the blocklist directory... */
2241  dirname = tr_buildPath (session->configDir, "blocklists", NULL);
2242  odir = opendir (dirname);
2243  while ((d = readdir (odir)))
2244    {
2245      char * path;
2246      char * load = NULL;
2247 
2248      if (!d->d_name || (d->d_name[0]=='.')) /* ignore dotfiles */
2249        continue;
2250
2251      path = tr_buildPath (dirname, d->d_name, NULL);
2252
2253      if (tr_stringEndsWith (path, ".bin"))
2254        {
2255          load = tr_strdup (path);
2256        }
2257      else
2258        {
2259          char * binname;
2260          char * basename;
2261          time_t path_mtime = 0;
2262          time_t binname_mtime = 0;
2263
2264          basename = tr_basename (d->d_name);
2265          binname = tr_strdup_printf ("%s" TR_PATH_DELIMITER_STR "%s.bin", dirname, basename);
2266
2267          if (!tr_fileExists (binname, &binname_mtime)) /* create it */
2268            {
2269              tr_blocklistFile * b = tr_blocklistFileNew (binname, isEnabled);
2270              const int n = tr_blocklistFileSetContent (b, path);
2271              if (n > 0)
2272                load = tr_strdup (binname);
2273
2274              tr_blocklistFileFree (b);
2275            }
2276          else if (tr_fileExists(path,&path_mtime) && (path_mtime>=binname_mtime)) /* update it */
2277            {
2278              char * old;
2279              tr_blocklistFile * b;
2280
2281              old = tr_strdup_printf ("%s.old", binname);
2282              tr_remove (old);
2283              tr_rename (binname, old);
2284              b = tr_blocklistFileNew (binname, isEnabled);
2285              if (tr_blocklistFileSetContent (b, path) > 0)
2286                {
2287                  tr_remove (old);
2288                }
2289              else
2290                {
2291                  tr_remove (binname);
2292                  tr_rename (old, binname);
2293                }
2294
2295              tr_blocklistFileFree (b);
2296              tr_free (old);
2297            }
2298
2299          tr_free (basename);
2300          tr_free (binname);
2301        }
2302
2303      if (load != NULL)
2304        {
2305          if (tr_ptrArrayFindSorted (&loadme, load, (PtrArrayCompareFunc)strcmp) == NULL)
2306            tr_ptrArrayInsertSorted (&loadme, load, (PtrArrayCompareFunc)strcmp);
2307          else
2308            tr_free (load);
2309        }
2310
2311      tr_free (path);
2312    }
2313
2314  if (!tr_ptrArrayEmpty (&loadme))
2315    {
2316      int i;
2317      const int n = tr_ptrArraySize (&loadme);
2318      const char ** paths = (const char **) tr_ptrArrayBase (&loadme);
2319
2320      for (i=0; i<n; ++i)
2321        tr_list_append (&blocklists, tr_blocklistFileNew (paths[i], isEnabled));
2322    }
2323
2324  /* cleanup */
2325  closedir (odir);
2326  tr_free (dirname);
2327  tr_ptrArrayDestruct (&loadme, (PtrArrayForeachFunc)tr_free);
2328  session->blocklists = blocklists;
2329}
2330
2331static void
2332closeBlocklists (tr_session * session)
2333{
2334  tr_list_free (&session->blocklists, (TrListForeachFunc)tr_blocklistFileFree);
2335}
2336
2337void
2338tr_sessionReloadBlocklists (tr_session * session)
2339{
2340  closeBlocklists (session);
2341  loadBlocklists (session);
2342
2343  tr_peerMgrOnBlocklistChanged (session->peerMgr);
2344}
2345
2346int
2347tr_blocklistGetRuleCount (const tr_session * session)
2348{
2349  tr_list * l;
2350  int n = 0;
2351
2352  assert (tr_isSession (session));
2353
2354  for (l = session->blocklists; l; l = l->next)
2355    n += tr_blocklistFileGetRuleCount (l->data);
2356
2357  return n;
2358}
2359
2360bool
2361tr_blocklistIsEnabled (const tr_session * session)
2362{
2363  assert (tr_isSession (session));
2364
2365  return session->isBlocklistEnabled;
2366}
2367
2368void
2369tr_blocklistSetEnabled (tr_session * session, bool isEnabled)
2370{
2371  tr_list * l;
2372
2373  assert (tr_isSession (session));
2374
2375  session->isBlocklistEnabled = isEnabled != 0;
2376
2377  for (l=session->blocklists; l!=NULL; l=l->next)
2378    tr_blocklistFileSetEnabled (l->data, isEnabled);
2379}
2380
2381bool
2382tr_blocklistExists (const tr_session * session)
2383{
2384  assert (tr_isSession (session));
2385
2386  return session->blocklists != NULL;
2387}
2388
2389int
2390tr_blocklistSetContent (tr_session * session, const char * contentFilename)
2391{
2392  tr_list * l;
2393  int ruleCount;
2394  tr_blocklistFile * b;
2395  const char * defaultName = DEFAULT_BLOCKLIST_FILENAME;
2396  tr_sessionLock (session);
2397
2398  for (b=NULL, l=session->blocklists; !b && l; l=l->next)
2399    if (tr_stringEndsWith (tr_blocklistFileGetFilename (l->data), defaultName))
2400      b = l->data;
2401
2402  if (!b)
2403    {
2404      char * path = tr_buildPath (session->configDir, "blocklists", defaultName, NULL);
2405      b = tr_blocklistFileNew (path, session->isBlocklistEnabled);
2406      tr_list_append (&session->blocklists, b);
2407      tr_free (path);
2408    }
2409
2410  ruleCount = tr_blocklistFileSetContent (b, contentFilename);
2411  tr_sessionUnlock (session);
2412  return ruleCount;
2413}
2414
2415bool
2416tr_sessionIsAddressBlocked (const tr_session * session,
2417                            const tr_address * addr)
2418{
2419  tr_list * l;
2420
2421  assert (tr_isSession (session));
2422
2423  for (l = session->blocklists; l; l = l->next)
2424    if (tr_blocklistFileHasAddress (l->data, addr))
2425      return true;
2426
2427  return false;
2428}
2429
2430void
2431tr_blocklistSetURL (tr_session * session, const char * url)
2432{
2433  if (session->blocklist_url != url)
2434    {
2435      tr_free (session->blocklist_url);
2436      session->blocklist_url = tr_strdup (url);
2437    }
2438}
2439
2440const char *
2441tr_blocklistGetURL (const tr_session * session)
2442{
2443  return session->blocklist_url;
2444}
2445
2446
2447/***
2448****
2449***/
2450
2451static void
2452metainfoLookupInit (tr_session * session)
2453{
2454  struct stat sb;
2455  const char * dirname = tr_getTorrentDir (session);
2456  DIR * odir = NULL;
2457  tr_ctor * ctor = NULL;
2458  tr_variant * lookup;
2459  int n = 0;
2460
2461  assert (tr_isSession (session));
2462
2463  /* walk through the directory and find the mappings */
2464  lookup = tr_new0 (tr_variant, 1);
2465  tr_variantInitDict (lookup, 0);
2466  ctor = tr_ctorNew (session);
2467  tr_ctorSetSave (ctor, false); /* since we already have them */
2468  if (!stat (dirname, &sb) && S_ISDIR (sb.st_mode) && ((odir = opendir (dirname))))
2469    {
2470      struct dirent *d;
2471      while ((d = readdir (odir)))
2472        {
2473          if (tr_str_has_suffix (d->d_name, ".torrent"))
2474            {
2475              tr_info inf;
2476              char * path = tr_buildPath (dirname, d->d_name, NULL);
2477              tr_ctorSetMetainfoFromFile (ctor, path);
2478              if (!tr_torrentParse (ctor, &inf))
2479                {
2480                  ++n;
2481                  tr_variantDictAddStr (lookup, tr_quark_new(inf.hashString,-1), path);
2482                }
2483              tr_free (path);
2484            }
2485        }
2486      closedir (odir);
2487    }
2488  tr_ctorFree (ctor);
2489
2490  session->metainfoLookup = lookup;
2491  tr_logAddDebug ("Found %d torrents in \"%s\"", n, dirname);
2492}
2493
2494const char*
2495tr_sessionFindTorrentFile (const tr_session * session,
2496                           const char       * hashString)
2497{
2498  const char * filename = NULL;
2499
2500  if (!session->metainfoLookup)
2501    metainfoLookupInit ((tr_session*)session);
2502  tr_variantDictFindStr (session->metainfoLookup, tr_quark_new(hashString,-1), &filename, NULL);
2503
2504  return filename;
2505}
2506
2507void
2508tr_sessionSetTorrentFile (tr_session * session,
2509                          const char * hashString,
2510                          const char * filename)
2511{
2512  /* since we walk session->configDir/torrents/ to build the lookup table,
2513   * and tr_sessionSetTorrentFile () is just to tell us there's a new file
2514   * in that same directory, we don't need to do anything here if the
2515   * lookup table hasn't been built yet */
2516  if (session->metainfoLookup)
2517    tr_variantDictAddStr (session->metainfoLookup, tr_quark_new(hashString,-1), filename);
2518}
2519
2520/***
2521****
2522***/
2523
2524void
2525tr_sessionSetRPCEnabled (tr_session * session, bool isEnabled)
2526{
2527  assert (tr_isSession (session));
2528
2529  tr_rpcSetEnabled (session->rpcServer, isEnabled);
2530}
2531
2532bool
2533tr_sessionIsRPCEnabled (const tr_session * session)
2534{
2535  assert (tr_isSession (session));
2536
2537  return tr_rpcIsEnabled (session->rpcServer);
2538}
2539
2540void
2541tr_sessionSetRPCPort (tr_session * session,
2542                      tr_port      port)
2543{
2544  assert (tr_isSession (session));
2545
2546  tr_rpcSetPort (session->rpcServer, port);
2547}
2548
2549tr_port
2550tr_sessionGetRPCPort (const tr_session * session)
2551{
2552  assert (tr_isSession (session));
2553
2554  return tr_rpcGetPort (session->rpcServer);
2555}
2556
2557void
2558tr_sessionSetRPCUrl (tr_session * session,
2559                     const char * url)
2560{
2561  assert (tr_isSession (session));
2562
2563  tr_rpcSetUrl (session->rpcServer, url);
2564}
2565
2566const char*
2567tr_sessionGetRPCUrl (const tr_session * session)
2568{
2569  assert (tr_isSession (session));
2570
2571  return tr_rpcGetUrl (session->rpcServer);
2572}
2573
2574void
2575tr_sessionSetRPCCallback (tr_session * session,
2576                          tr_rpc_func  func,
2577                          void *       user_data)
2578{
2579  assert (tr_isSession (session));
2580
2581  session->rpc_func = func;
2582  session->rpc_func_user_data = user_data;
2583}
2584
2585void
2586tr_sessionSetRPCWhitelist (tr_session * session,
2587                           const char * whitelist)
2588{
2589  assert (tr_isSession (session));
2590
2591  tr_rpcSetWhitelist (session->rpcServer, whitelist);
2592}
2593
2594const char*
2595tr_sessionGetRPCWhitelist (const tr_session * session)
2596{
2597  assert (tr_isSession (session));
2598
2599  return tr_rpcGetWhitelist (session->rpcServer);
2600}
2601
2602void
2603tr_sessionSetRPCWhitelistEnabled (tr_session * session, bool isEnabled)
2604{
2605  assert (tr_isSession (session));
2606
2607  tr_rpcSetWhitelistEnabled (session->rpcServer, isEnabled);
2608}
2609
2610bool
2611tr_sessionGetRPCWhitelistEnabled (const tr_session * session)
2612{
2613  assert (tr_isSession (session));
2614
2615  return tr_rpcGetWhitelistEnabled (session->rpcServer);
2616}
2617
2618
2619void
2620tr_sessionSetRPCPassword (tr_session * session,
2621                          const char * password)
2622{
2623  assert (tr_isSession (session));
2624
2625  tr_rpcSetPassword (session->rpcServer, password);
2626}
2627
2628const char*
2629tr_sessionGetRPCPassword (const tr_session * session)
2630{
2631  assert (tr_isSession (session));
2632
2633  return tr_rpcGetPassword (session->rpcServer);
2634}
2635
2636void
2637tr_sessionSetRPCUsername (tr_session * session,
2638                          const char * username)
2639{
2640  assert (tr_isSession (session));
2641
2642  tr_rpcSetUsername (session->rpcServer, username);
2643}
2644
2645const char*
2646tr_sessionGetRPCUsername (const tr_session * session)
2647{
2648  assert (tr_isSession (session));
2649
2650  return tr_rpcGetUsername (session->rpcServer);
2651}
2652
2653void
2654tr_sessionSetRPCPasswordEnabled (tr_session * session, bool isEnabled)
2655{
2656  assert (tr_isSession (session));
2657
2658  tr_rpcSetPasswordEnabled (session->rpcServer, isEnabled);
2659}
2660
2661bool
2662tr_sessionIsRPCPasswordEnabled (const tr_session * session)
2663{
2664  assert (tr_isSession (session));
2665
2666  return tr_rpcIsPasswordEnabled (session->rpcServer);
2667}
2668
2669const char *
2670tr_sessionGetRPCBindAddress (const tr_session * session)
2671{
2672  assert (tr_isSession (session));
2673
2674  return tr_rpcGetBindAddress (session->rpcServer);
2675}
2676
2677/****
2678*****
2679****/
2680
2681bool
2682tr_sessionIsTorrentDoneScriptEnabled (const tr_session * session)
2683{
2684  assert (tr_isSession (session));
2685
2686  return session->isTorrentDoneScriptEnabled;
2687}
2688
2689void
2690tr_sessionSetTorrentDoneScriptEnabled (tr_session * session, bool isEnabled)
2691{
2692  assert (tr_isSession (session));
2693  assert (tr_isBool (isEnabled));
2694
2695  session->isTorrentDoneScriptEnabled = isEnabled;
2696}
2697
2698const char *
2699tr_sessionGetTorrentDoneScript (const tr_session * session)
2700{
2701  assert (tr_isSession (session));
2702
2703  return session->torrentDoneScript;
2704}
2705
2706void
2707tr_sessionSetTorrentDoneScript (tr_session * session, const char * scriptFilename)
2708{
2709  assert (tr_isSession (session));
2710
2711  if (session->torrentDoneScript != scriptFilename)
2712    {
2713      tr_free (session->torrentDoneScript);
2714      session->torrentDoneScript = tr_strdup (scriptFilename);
2715    }
2716}
2717
2718/***
2719****
2720***/
2721
2722void
2723tr_sessionSetQueueSize (tr_session * session, tr_direction dir, int n)
2724{
2725  assert (tr_isSession (session));
2726  assert (tr_isDirection (dir));
2727
2728  session->queueSize[dir] = n;
2729}
2730
2731int
2732tr_sessionGetQueueSize (const tr_session * session, tr_direction dir)
2733{
2734  assert (tr_isSession (session));
2735  assert (tr_isDirection (dir));
2736
2737  return session->queueSize[dir];
2738}
2739
2740void
2741tr_sessionSetQueueEnabled (tr_session * session, tr_direction dir, bool is_enabled)
2742{
2743  assert (tr_isSession (session));
2744  assert (tr_isDirection (dir));
2745  assert (tr_isBool (is_enabled));
2746
2747  session->queueEnabled[dir] = is_enabled;
2748}
2749
2750bool
2751tr_sessionGetQueueEnabled (const tr_session * session, tr_direction dir)
2752{
2753  assert (tr_isSession (session));
2754  assert (tr_isDirection (dir));
2755
2756  return session->queueEnabled[dir];
2757}
2758
2759void
2760tr_sessionSetQueueStalledMinutes (tr_session * session, int minutes)
2761{
2762  assert (tr_isSession (session));
2763  assert (minutes > 0);
2764
2765  session->queueStalledMinutes = minutes;
2766}
2767
2768void
2769tr_sessionSetQueueStalledEnabled (tr_session * session, bool is_enabled)
2770{
2771  assert (tr_isSession (session));
2772  assert (tr_isBool (is_enabled));
2773
2774  session->stalledEnabled = is_enabled;
2775}
2776
2777bool
2778tr_sessionGetQueueStalledEnabled (const tr_session * session)
2779{
2780  assert (tr_isSession (session));
2781
2782  return session->stalledEnabled;
2783}
2784
2785int
2786tr_sessionGetQueueStalledMinutes (const tr_session * session)
2787{
2788  assert (tr_isSession (session));
2789
2790  return session->queueStalledMinutes;
2791}
2792
2793struct TorrentAndPosition
2794{
2795  tr_torrent * tor;
2796  int position;
2797};
2798
2799/* higher positions come first */
2800static int
2801compareTorrentAndPositions (const void * va, const void * vb)
2802{
2803  int ret;
2804  const struct TorrentAndPosition * a = va;
2805  const struct TorrentAndPosition * b = vb;
2806
2807  if (a->position > b->position)
2808    ret = -1;
2809  else if (a->position < b->position)
2810    ret = 1;
2811  else
2812    ret = 0;
2813
2814  return ret;
2815}
2816
2817
2818void
2819tr_sessionGetNextQueuedTorrents (tr_session   * session,
2820                                 tr_direction   direction,
2821                                 size_t         num_wanted,
2822                                 tr_ptrArray  * setme)
2823{
2824  size_t i;
2825  size_t n;
2826  tr_torrent * tor;
2827  struct TorrentAndPosition * candidates;
2828
2829  assert (tr_isSession (session));
2830  assert (tr_isDirection (direction));
2831
2832  /* build an array of the candidates */
2833  n = tr_sessionCountTorrents (session);
2834  candidates = tr_new (struct TorrentAndPosition, n);
2835  i = 0;
2836  tor = NULL;
2837  while ((tor = tr_torrentNext (session, tor)))
2838    {
2839      if (!tr_torrentIsQueued (tor))
2840        continue;
2841
2842      if (direction != tr_torrentGetQueueDirection (tor))
2843        continue;
2844
2845      candidates[i].tor = tor;
2846      candidates[i].position = tr_torrentGetQueuePosition (tor);
2847      ++i;
2848    }
2849
2850  /* find the best n candidates */
2851  if (num_wanted > i)
2852    num_wanted = i;
2853  else if (num_wanted < i)
2854    tr_quickfindFirstK (candidates, i,
2855                        sizeof(struct TorrentAndPosition),
2856                        compareTorrentAndPositions, num_wanted);
2857
2858  /* add them to the return array */
2859  for (i=0; i<num_wanted; ++i)
2860    tr_ptrArrayAppend (setme, candidates[i].tor);
2861
2862  /* cleanup */
2863  tr_free (candidates);
2864}
2865
2866int
2867tr_sessionCountQueueFreeSlots (tr_session * session, tr_direction dir)
2868{
2869  tr_torrent * tor;
2870  int active_count;
2871  const int max = tr_sessionGetQueueSize (session, dir);
2872  const tr_torrent_activity activity = dir == TR_UP ? TR_STATUS_SEED : TR_STATUS_DOWNLOAD;
2873
2874  if (!tr_sessionGetQueueEnabled (session, dir))
2875    return INT_MAX;
2876
2877    tor = NULL;
2878    active_count = 0;
2879    while ((tor = tr_torrentNext (session, tor)))
2880        if (!tr_torrentIsStalled (tor))
2881            if (tr_torrentGetActivity (tor) == activity)
2882                ++active_count;
2883
2884    if (active_count >= max)
2885        return 0;
2886
2887    return max - active_count;
2888}
Note: See TracBrowser for help on using the repository browser.