source: trunk/libtransmission/session.c @ 14382

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

Fix compilation on Windows

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

  • Property svn:keywords set to Date Rev Author Id
File size: 75.9 KB
Line 
1/*
2 * This file Copyright (C) 2008-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: session.c 14382 2014-12-13 15:22:39Z mikedld $
8 */
9
10#include <assert.h>
11#include <errno.h> /* ENOENT */
12#include <limits.h> /* INT_MAX */
13#include <stdlib.h>
14#include <string.h> /* memcpy */
15
16#include <signal.h>
17
18#ifndef _WIN32
19 #include <sys/types.h> /* umask () */
20 #include <sys/stat.h> /* umask () */
21#endif
22
23#include <event2/dns.h> /* evdns_base_free () */
24#include <event2/event.h>
25
26#include <libutp/utp.h>
27
28//#define TR_SHOW_DEPRECATED
29#include "transmission.h"
30#include "announcer.h"
31#include "bandwidth.h"
32#include "blocklist.h"
33#include "cache.h"
34#include "crypto-utils.h"
35#include "fdlimit.h"
36#include "file.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_rand_int_weak (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_rand_buffer (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 (evutil_socket_t 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 (evutil_socket_t 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 (evutil_socket_t 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  tr_gettimeofday (&tv);
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 %"TR_PRIuSIZE" sec, %"TR_PRIuSIZE" 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          (void*)&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_sys_dir_create (filename, TR_SYS_DIR_CREATE_PARENTS, 0777, NULL);
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#ifndef _WIN32
778  if (tr_variantDictFindInt (settings, TR_KEY_umask, &i))
779    {
780      session->umask = (mode_t)i;
781      umask (session->umask);
782    }
783#endif
784
785  /* misc features */
786  if (tr_variantDictFindInt (settings, TR_KEY_cache_size_mb, &i))
787    tr_sessionSetCacheLimit_MB (session, i);
788  if (tr_variantDictFindInt (settings, TR_KEY_peer_limit_per_torrent, &i))
789    tr_sessionSetPeerLimitPerTorrent (session, i);
790  if (tr_variantDictFindBool (settings, TR_KEY_pex_enabled, &boolVal))
791    tr_sessionSetPexEnabled (session, boolVal);
792  if (tr_variantDictFindBool (settings, TR_KEY_dht_enabled, &boolVal))
793    tr_sessionSetDHTEnabled (session, boolVal);
794  if (tr_variantDictFindBool (settings, TR_KEY_utp_enabled, &boolVal))
795    tr_sessionSetUTPEnabled (session, boolVal);
796  if (tr_variantDictFindBool (settings, TR_KEY_lpd_enabled, &boolVal))
797    tr_sessionSetLPDEnabled (session, boolVal);
798  if (tr_variantDictFindInt (settings, TR_KEY_encryption, &i))
799    tr_sessionSetEncryption (session, i);
800  if (tr_variantDictFindStr (settings, TR_KEY_peer_socket_tos, &str, NULL))
801    session->peerSocketTOS = parse_tos (str);
802  if (tr_variantDictFindStr (settings, TR_KEY_peer_congestion_algorithm, &str, NULL))
803    session->peer_congestion_algorithm = tr_strdup (str);
804  else
805    session->peer_congestion_algorithm = tr_strdup ("");
806  if (tr_variantDictFindBool (settings, TR_KEY_blocklist_enabled, &boolVal))
807    tr_blocklistSetEnabled (session, boolVal);
808  if (tr_variantDictFindStr (settings, TR_KEY_blocklist_url, &str, NULL))
809    tr_blocklistSetURL (session, str);
810  if (tr_variantDictFindBool (settings, TR_KEY_start_added_torrents, &boolVal))
811    tr_sessionSetPaused (session, !boolVal);
812  if (tr_variantDictFindBool (settings, TR_KEY_trash_original_torrent_files, &boolVal))
813    tr_sessionSetDeleteSource (session, boolVal);
814  if (tr_variantDictFindInt (settings, TR_KEY_peer_id_ttl_hours, &i))
815    session->peer_id_ttl_hours = i;
816
817  /* torrent queues */
818  if (tr_variantDictFindInt (settings, TR_KEY_queue_stalled_minutes, &i))
819    tr_sessionSetQueueStalledMinutes (session, i);
820  if (tr_variantDictFindBool (settings, TR_KEY_queue_stalled_enabled, &boolVal))
821    tr_sessionSetQueueStalledEnabled (session, boolVal);
822  if (tr_variantDictFindInt (settings, TR_KEY_download_queue_size, &i))
823    tr_sessionSetQueueSize (session, TR_DOWN, i);
824  if (tr_variantDictFindBool (settings, TR_KEY_download_queue_enabled, &boolVal))
825    tr_sessionSetQueueEnabled (session, TR_DOWN, boolVal);
826  if (tr_variantDictFindInt (settings, TR_KEY_seed_queue_size, &i))
827    tr_sessionSetQueueSize (session, TR_UP, i);
828  if (tr_variantDictFindBool (settings, TR_KEY_seed_queue_enabled, &boolVal))
829    tr_sessionSetQueueEnabled (session, TR_UP, boolVal);
830
831  /* files and directories */
832  if (tr_variantDictFindBool (settings, TR_KEY_prefetch_enabled, &boolVal))
833    session->isPrefetchEnabled = boolVal;
834  if (tr_variantDictFindInt (settings, TR_KEY_preallocation, &i))
835    session->preallocationMode = i;
836  if (tr_variantDictFindStr (settings, TR_KEY_download_dir, &str, NULL))
837    tr_sessionSetDownloadDir (session, str);
838  if (tr_variantDictFindStr (settings, TR_KEY_incomplete_dir, &str, NULL))
839    tr_sessionSetIncompleteDir (session, str);
840  if (tr_variantDictFindBool (settings, TR_KEY_incomplete_dir_enabled, &boolVal))
841    tr_sessionSetIncompleteDirEnabled (session, boolVal);
842  if (tr_variantDictFindBool (settings, TR_KEY_rename_partial_files, &boolVal))
843    tr_sessionSetIncompleteFileNamingEnabled (session, boolVal);
844
845  /* rpc server */
846  if (session->rpcServer != NULL) /* close the old one */
847    tr_rpcClose (&session->rpcServer);
848  session->rpcServer = tr_rpcInit (session, settings);
849
850  /* public addresses */
851
852  free_incoming_peer_port (session);
853
854  tr_variantDictFindStr (settings, TR_KEY_bind_address_ipv4, &str, NULL);
855  if (!tr_address_from_string (&b.addr, str) || (b.addr.type != TR_AF_INET))
856    b.addr = tr_inaddr_any;
857  b.socket = -1;
858  session->public_ipv4 = tr_memdup (&b, sizeof (struct tr_bindinfo));
859
860  tr_variantDictFindStr (settings, TR_KEY_bind_address_ipv6, &str, NULL);
861  if (!tr_address_from_string (&b.addr, str) || (b.addr.type != TR_AF_INET6))
862    b.addr = tr_in6addr_any;
863  b.socket = -1;
864  session->public_ipv6 = tr_memdup (&b, sizeof (struct tr_bindinfo));
865
866  /* incoming peer port */
867  if (tr_variantDictFindInt (settings, TR_KEY_peer_port_random_low, &i))
868    session->randomPortLow = i;
869  if (tr_variantDictFindInt (settings, TR_KEY_peer_port_random_high, &i))
870    session->randomPortHigh = i;
871  if (tr_variantDictFindBool (settings, TR_KEY_peer_port_random_on_start, &boolVal))
872    tr_sessionSetPeerPortRandomOnStart (session, boolVal);
873  if (!tr_variantDictFindInt (settings, TR_KEY_peer_port, &i))
874    i = session->private_peer_port;
875  setPeerPort (session, boolVal ? getRandomPort (session) : i);
876  if (tr_variantDictFindBool (settings, TR_KEY_port_forwarding_enabled, &boolVal))
877    tr_sessionSetPortForwardingEnabled (session, boolVal);
878
879  if (tr_variantDictFindInt (settings, TR_KEY_peer_limit_global, &i))
880    session->peerLimit = i;
881
882  /**
883  **/
884
885  if (tr_variantDictFindInt (settings, TR_KEY_upload_slots_per_torrent, &i))
886    session->uploadSlotsPerTorrent = i;
887
888  if (tr_variantDictFindInt (settings, TR_KEY_speed_limit_up, &i))
889    tr_sessionSetSpeedLimit_KBps (session, TR_UP, i);
890  if (tr_variantDictFindBool (settings, TR_KEY_speed_limit_up_enabled, &boolVal))
891    tr_sessionLimitSpeed (session, TR_UP, boolVal);
892
893  if (tr_variantDictFindInt (settings, TR_KEY_speed_limit_down, &i))
894    tr_sessionSetSpeedLimit_KBps (session, TR_DOWN, i);
895  if (tr_variantDictFindBool (settings, TR_KEY_speed_limit_down_enabled, &boolVal))
896    tr_sessionLimitSpeed (session, TR_DOWN, boolVal);
897
898  if (tr_variantDictFindReal (settings, TR_KEY_ratio_limit, &d))
899    tr_sessionSetRatioLimit (session, d);
900  if (tr_variantDictFindBool (settings, TR_KEY_ratio_limit_enabled, &boolVal))
901    tr_sessionSetRatioLimited (session, boolVal);
902
903  if (tr_variantDictFindInt (settings, TR_KEY_idle_seeding_limit, &i))
904    tr_sessionSetIdleLimit (session, i);
905  if (tr_variantDictFindBool (settings, TR_KEY_idle_seeding_limit_enabled, &boolVal))
906    tr_sessionSetIdleLimited (session, boolVal);
907
908  /**
909  ***  Turtle Mode
910  **/
911
912  /* update the turtle mode's fields */
913  if (tr_variantDictFindInt (settings, TR_KEY_alt_speed_up, &i))
914    turtle->speedLimit_Bps[TR_UP] = toSpeedBytes (i);
915  if (tr_variantDictFindInt (settings, TR_KEY_alt_speed_down, &i))
916    turtle->speedLimit_Bps[TR_DOWN] = toSpeedBytes (i);
917  if (tr_variantDictFindInt (settings, TR_KEY_alt_speed_time_begin, &i))
918    turtle->beginMinute = i;
919  if (tr_variantDictFindInt (settings, TR_KEY_alt_speed_time_end, &i))
920    turtle->endMinute = i;
921  if (tr_variantDictFindInt (settings, TR_KEY_alt_speed_time_day, &i))
922    turtle->days = i;
923  if (tr_variantDictFindBool (settings, TR_KEY_alt_speed_time_enabled, &boolVal))
924    turtle->isClockEnabled = boolVal;
925  if (tr_variantDictFindBool (settings, TR_KEY_alt_speed_enabled, &boolVal))
926    turtle->isEnabled = boolVal;
927  turtleBootstrap (session, turtle);
928
929  /**
930  ***  Scripts
931  **/
932
933  if (tr_variantDictFindBool (settings, TR_KEY_script_torrent_done_enabled, &boolVal))
934    tr_sessionSetTorrentDoneScriptEnabled (session, boolVal);
935  if (tr_variantDictFindStr (settings, TR_KEY_script_torrent_done_filename, &str, NULL))
936    tr_sessionSetTorrentDoneScript (session, str);
937
938
939  if (tr_variantDictFindBool (settings, TR_KEY_scrape_paused_torrents_enabled, &boolVal))
940    session->scrapePausedTorrents = boolVal;
941
942  data->done = true;
943}
944
945void
946tr_sessionSet (tr_session * session, tr_variant * settings)
947{
948  struct init_data data;
949  data.done = false;
950  data.session = session;
951  data.clientSettings = settings;
952
953  /* run the rest in the libtransmission thread */
954  tr_runInEventThread (session, sessionSetImpl, &data);
955  while (!data.done)
956    tr_wait_msec (100);
957}
958
959/***
960****
961***/
962
963void
964tr_sessionSetDownloadDir (tr_session * session, const char * dir)
965{
966  struct tr_device_info * info = NULL;
967
968  assert (tr_isSession (session));
969
970  if (dir != NULL)
971    info = tr_device_info_create (dir);
972  tr_device_info_free (session->downloadDir);
973  session->downloadDir = info;
974}
975
976const char *
977tr_sessionGetDownloadDir (const tr_session * session)
978{
979  const char * dir = NULL;
980
981  assert (tr_isSession (session));
982
983  if ((session != NULL) && (session->downloadDir != NULL))
984    dir = session->downloadDir->path;
985
986  return dir;
987}
988
989int64_t
990tr_sessionGetDirFreeSpace (tr_session * session, const char * dir)
991{
992  int64_t free_space;
993
994  if (!tr_strcmp0 (dir, tr_sessionGetDownloadDir (session)))
995    free_space = tr_device_info_get_free_space (session->downloadDir);
996  else
997    free_space = tr_getDirFreeSpace (dir);
998
999  return free_space;
1000}
1001
1002/***
1003****
1004***/
1005
1006void
1007tr_sessionSetIncompleteFileNamingEnabled (tr_session * session, bool b)
1008{
1009  assert (tr_isSession (session));
1010  assert (tr_isBool (b));
1011
1012  session->isIncompleteFileNamingEnabled = b;
1013}
1014
1015bool
1016tr_sessionIsIncompleteFileNamingEnabled (const tr_session * session)
1017{
1018  assert (tr_isSession (session));
1019
1020  return session->isIncompleteFileNamingEnabled;
1021}
1022
1023/***
1024****
1025***/
1026
1027
1028void
1029tr_sessionSetIncompleteDir (tr_session * session, const char * dir)
1030{
1031  assert (tr_isSession (session));
1032
1033  if (session->incompleteDir != dir)
1034    {
1035      tr_free (session->incompleteDir);
1036
1037      session->incompleteDir = tr_strdup (dir);
1038    }
1039}
1040
1041const char*
1042tr_sessionGetIncompleteDir (const tr_session * session)
1043{
1044  assert (tr_isSession (session));
1045
1046  return session->incompleteDir;
1047}
1048
1049void
1050tr_sessionSetIncompleteDirEnabled (tr_session * session, bool b)
1051{
1052  assert (tr_isSession (session));
1053  assert (tr_isBool (b));
1054
1055  session->isIncompleteDirEnabled = b;
1056}
1057
1058bool
1059tr_sessionIsIncompleteDirEnabled (const tr_session * session)
1060{
1061  assert (tr_isSession (session));
1062
1063  return session->isIncompleteDirEnabled;
1064}
1065
1066/***
1067****
1068***/
1069
1070void
1071tr_sessionLock (tr_session * session)
1072{
1073  assert (tr_isSession (session));
1074
1075  tr_lockLock (session->lock);
1076}
1077
1078void
1079tr_sessionUnlock (tr_session * session)
1080{
1081  assert (tr_isSession (session));
1082
1083  tr_lockUnlock (session->lock);
1084}
1085
1086bool
1087tr_sessionIsLocked (const tr_session * session)
1088{
1089  return tr_isSession (session) && tr_lockHave (session->lock);
1090}
1091
1092/***
1093****  Peer Port
1094***/
1095
1096static void
1097peerPortChanged (void * session)
1098{
1099  tr_torrent * tor = NULL;
1100
1101  assert (tr_isSession (session));
1102
1103  close_incoming_peer_port (session);
1104  open_incoming_peer_port (session);
1105  tr_sharedPortChanged (session);
1106
1107  while ((tor = tr_torrentNext (session, tor)))
1108    tr_torrentChangeMyPort (tor);
1109}
1110
1111static void
1112setPeerPort (tr_session * session, tr_port port)
1113{
1114  session->private_peer_port = port;
1115  session->public_peer_port = port;
1116
1117  tr_runInEventThread (session, peerPortChanged, session);
1118}
1119
1120void
1121tr_sessionSetPeerPort (tr_session * session, tr_port port)
1122{
1123  if (tr_isSession (session) && (session->private_peer_port != port))
1124    {
1125      setPeerPort (session, port);
1126    }
1127}
1128
1129tr_port
1130tr_sessionGetPeerPort (const tr_session * session)
1131{
1132  return tr_isSession (session) ? session->private_peer_port : 0;
1133}
1134
1135tr_port
1136tr_sessionSetPeerPortRandom (tr_session * session)
1137{
1138  assert (tr_isSession (session));
1139
1140  tr_sessionSetPeerPort (session, getRandomPort (session));
1141  return session->private_peer_port;
1142}
1143
1144void
1145tr_sessionSetPeerPortRandomOnStart (tr_session * session,
1146                                    bool random)
1147{
1148  assert (tr_isSession (session));
1149
1150  session->isPortRandom = random;
1151}
1152
1153bool
1154tr_sessionGetPeerPortRandomOnStart (tr_session * session)
1155{
1156  assert (tr_isSession (session));
1157
1158  return session->isPortRandom;
1159}
1160
1161tr_port_forwarding
1162tr_sessionGetPortForwarding (const tr_session * session)
1163{
1164  assert (tr_isSession (session));
1165
1166  return tr_sharedTraversalStatus (session->shared);
1167}
1168
1169/***
1170****
1171***/
1172
1173void
1174tr_sessionSetRatioLimited (tr_session * session, bool isLimited)
1175{
1176  assert (tr_isSession (session));
1177
1178  session->isRatioLimited = isLimited;
1179}
1180
1181void
1182tr_sessionSetRatioLimit (tr_session * session, double desiredRatio)
1183{
1184  assert (tr_isSession (session));
1185
1186  session->desiredRatio = desiredRatio;
1187}
1188
1189bool
1190tr_sessionIsRatioLimited (const tr_session  * session)
1191{
1192  assert (tr_isSession (session));
1193
1194  return session->isRatioLimited;
1195}
1196
1197double
1198tr_sessionGetRatioLimit (const tr_session * session)
1199{
1200  assert (tr_isSession (session));
1201
1202  return session->desiredRatio;
1203}
1204
1205/***
1206****
1207***/
1208
1209void
1210tr_sessionSetIdleLimited (tr_session * session, bool isLimited)
1211{
1212  assert (tr_isSession (session));
1213
1214  session->isIdleLimited = isLimited;
1215}
1216
1217void
1218tr_sessionSetIdleLimit (tr_session * session, uint16_t idleMinutes)
1219{
1220  assert (tr_isSession (session));
1221
1222  session->idleLimitMinutes = idleMinutes;
1223}
1224
1225bool
1226tr_sessionIsIdleLimited (const tr_session  * session)
1227{
1228  assert (tr_isSession (session));
1229
1230  return session->isIdleLimited;
1231}
1232
1233uint16_t
1234tr_sessionGetIdleLimit (const tr_session * session)
1235{
1236  assert (tr_isSession (session));
1237
1238  return session->idleLimitMinutes;
1239}
1240
1241/***
1242****
1243****  SPEED LIMITS
1244****
1245***/
1246
1247bool
1248tr_sessionGetActiveSpeedLimit_Bps (const tr_session * session, tr_direction dir, unsigned int * setme_Bps)
1249{
1250  bool isLimited = true;
1251
1252  if (!tr_isSession (session))
1253    return false;
1254
1255  if (tr_sessionUsesAltSpeed (session))
1256    *setme_Bps = tr_sessionGetAltSpeed_Bps (session, dir);
1257  else if (tr_sessionIsSpeedLimited (session, dir))
1258    *setme_Bps = tr_sessionGetSpeedLimit_Bps (session, dir);
1259  else
1260    isLimited = false;
1261
1262  return isLimited;
1263}
1264bool
1265tr_sessionGetActiveSpeedLimit_KBps (const tr_session  * session,
1266                                    tr_direction        dir,
1267                                    double            * setme_KBps)
1268{
1269  unsigned int Bps = 0;
1270  const bool is_active = tr_sessionGetActiveSpeedLimit_Bps (session, dir, &Bps);
1271  *setme_KBps = toSpeedKBps (Bps);
1272  return is_active;
1273}
1274
1275static void
1276updateBandwidth (tr_session * session, tr_direction dir)
1277{
1278  unsigned int limit_Bps = 0;
1279  const bool isLimited = tr_sessionGetActiveSpeedLimit_Bps (session, dir, &limit_Bps);
1280  const bool zeroCase = isLimited && !limit_Bps;
1281
1282  tr_bandwidthSetLimited (&session->bandwidth, dir, isLimited && !zeroCase);
1283
1284  tr_bandwidthSetDesiredSpeed_Bps (&session->bandwidth, dir, limit_Bps);
1285}
1286
1287enum
1288{
1289  MINUTES_PER_HOUR = 60,
1290  MINUTES_PER_DAY = MINUTES_PER_HOUR * 24,
1291  MINUTES_PER_WEEK = MINUTES_PER_DAY * 7
1292};
1293
1294static void
1295turtleUpdateTable (struct tr_turtle_info * t)
1296{
1297  int day;
1298  tr_bitfield * b = &t->minutes;
1299
1300  tr_bitfieldSetHasNone (b);
1301
1302  for (day=0; day<7; ++day)
1303    {
1304      if (t->days & (1<<day))
1305        {
1306          int i;
1307          const time_t begin = t->beginMinute;
1308          time_t end = t->endMinute;
1309
1310          if (end <= begin)
1311            end += MINUTES_PER_DAY;
1312
1313          for (i=begin; i<end; ++i)
1314            tr_bitfieldAdd (b, (i+day*MINUTES_PER_DAY) % MINUTES_PER_WEEK);
1315        }
1316    }
1317}
1318
1319static void
1320altSpeedToggled (void * vsession)
1321{
1322  tr_session * session = vsession;
1323  struct tr_turtle_info * t = &session->turtle;
1324
1325  assert (tr_isSession (session));
1326
1327  updateBandwidth (session, TR_UP);
1328  updateBandwidth (session, TR_DOWN);
1329
1330  if (t->callback != NULL)
1331    (*t->callback)(session, t->isEnabled, t->changedByUser, t->callbackUserData);
1332}
1333
1334static void
1335useAltSpeed (tr_session * s, struct tr_turtle_info * t,
1336             bool enabled, bool byUser)
1337{
1338  assert (tr_isSession (s));
1339  assert (t != NULL);
1340  assert (tr_isBool (enabled));
1341  assert (tr_isBool (byUser));
1342
1343  if (t->isEnabled != enabled)
1344    {
1345      t->isEnabled = enabled;
1346      t->changedByUser = byUser;
1347      tr_runInEventThread (s, altSpeedToggled, s);
1348    }
1349}
1350
1351/**
1352 * @return whether turtle should be on/off according to the scheduler
1353 */
1354static bool
1355getInTurtleTime (const struct tr_turtle_info * t)
1356{
1357  struct tm tm;
1358  size_t minute_of_the_week;
1359  const time_t now = tr_time ();
1360
1361  tr_localtime_r (&now, &tm);
1362
1363  minute_of_the_week = tm.tm_wday * MINUTES_PER_DAY
1364                     + tm.tm_hour * MINUTES_PER_HOUR
1365                     + tm.tm_min;
1366  if (minute_of_the_week >= MINUTES_PER_WEEK) /* leap minutes? */
1367    minute_of_the_week = MINUTES_PER_WEEK - 1;
1368
1369  return tr_bitfieldHas (&t->minutes, minute_of_the_week);
1370}
1371
1372static inline tr_auto_switch_state_t
1373autoSwitchState (bool enabled)
1374{
1375    return enabled ? TR_AUTO_SWITCH_ON : TR_AUTO_SWITCH_OFF;
1376}
1377
1378static void
1379turtleCheckClock (tr_session * s, struct tr_turtle_info * t)
1380{
1381  bool enabled;
1382  bool alreadySwitched;
1383  tr_auto_switch_state_t newAutoTurtleState;
1384
1385  assert (t->isClockEnabled);
1386
1387  enabled = getInTurtleTime (t);
1388  newAutoTurtleState = autoSwitchState (enabled);
1389  alreadySwitched = (t->autoTurtleState == newAutoTurtleState);
1390
1391  if (!alreadySwitched)
1392    {
1393      tr_logAddInfo ("Time to turn %s turtle mode!", (enabled?"on":"off"));
1394      t->autoTurtleState = newAutoTurtleState;
1395      useAltSpeed (s, t, enabled, false);
1396    }
1397}
1398
1399/* Called after the turtle's fields are loaded from an outside source.
1400 * It initializes the implementation fields
1401 * and turns on turtle mode if the clock settings say to. */
1402static void
1403turtleBootstrap (tr_session * session, struct tr_turtle_info * turtle)
1404{
1405  turtle->changedByUser = false;
1406  turtle->autoTurtleState = TR_AUTO_SWITCH_UNUSED;
1407
1408  tr_bitfieldConstruct (&turtle->minutes, MINUTES_PER_WEEK);
1409
1410  turtleUpdateTable (turtle);
1411
1412  if (turtle->isClockEnabled)
1413    {
1414      turtle->isEnabled = getInTurtleTime (turtle);
1415      turtle->autoTurtleState = autoSwitchState (turtle->isEnabled);
1416    }
1417
1418  altSpeedToggled (session);
1419
1420}
1421
1422/***
1423****  Primary session speed limits
1424***/
1425
1426void
1427tr_sessionSetSpeedLimit_Bps (tr_session * s, tr_direction d, unsigned int Bps)
1428{
1429  assert (tr_isSession (s));
1430  assert (tr_isDirection (d));
1431
1432  s->speedLimit_Bps[d] = Bps;
1433
1434  updateBandwidth (s, d);
1435}
1436void
1437tr_sessionSetSpeedLimit_KBps (tr_session * s, tr_direction d, unsigned int KBps)
1438{
1439  tr_sessionSetSpeedLimit_Bps (s, d, toSpeedBytes (KBps));
1440}
1441
1442unsigned int
1443tr_sessionGetSpeedLimit_Bps (const tr_session * s, tr_direction d)
1444{
1445  assert (tr_isSession (s));
1446  assert (tr_isDirection (d));
1447
1448  return s->speedLimit_Bps[d];
1449}
1450unsigned int
1451tr_sessionGetSpeedLimit_KBps (const tr_session * s, tr_direction d)
1452{
1453    return toSpeedKBps (tr_sessionGetSpeedLimit_Bps (s, d));
1454}
1455
1456void
1457tr_sessionLimitSpeed (tr_session * s, tr_direction d, bool b)
1458{
1459  assert (tr_isSession (s));
1460  assert (tr_isDirection (d));
1461  assert (tr_isBool (b));
1462
1463  s->speedLimitEnabled[d] = b;
1464
1465  updateBandwidth (s, d);
1466}
1467
1468bool
1469tr_sessionIsSpeedLimited (const tr_session * s, tr_direction d)
1470{
1471  assert (tr_isSession (s));
1472  assert (tr_isDirection (d));
1473
1474  return s->speedLimitEnabled[d];
1475}
1476
1477/***
1478****  Alternative speed limits that are used during scheduled times
1479***/
1480
1481void
1482tr_sessionSetAltSpeed_Bps (tr_session * s, tr_direction d, unsigned int Bps)
1483{
1484  assert (tr_isSession (s));
1485  assert (tr_isDirection (d));
1486
1487  s->turtle.speedLimit_Bps[d] = Bps;
1488
1489  updateBandwidth (s, d);
1490}
1491
1492void
1493tr_sessionSetAltSpeed_KBps (tr_session * s, tr_direction d, unsigned int KBps)
1494{
1495  tr_sessionSetAltSpeed_Bps (s, d, toSpeedBytes (KBps));
1496}
1497
1498unsigned int
1499tr_sessionGetAltSpeed_Bps (const tr_session * s, tr_direction d)
1500{
1501  assert (tr_isSession (s));
1502  assert (tr_isDirection (d));
1503
1504  return s->turtle.speedLimit_Bps[d];
1505}
1506unsigned int
1507tr_sessionGetAltSpeed_KBps (const tr_session * s, tr_direction d)
1508{
1509  return toSpeedKBps (tr_sessionGetAltSpeed_Bps (s, d));
1510}
1511
1512static void
1513userPokedTheClock (tr_session * s, struct tr_turtle_info * t)
1514{
1515  tr_logAddDebug ("Refreshing the turtle mode clock due to user changes");
1516
1517  t->autoTurtleState = TR_AUTO_SWITCH_UNUSED;
1518
1519  turtleUpdateTable (t);
1520
1521  if (t->isClockEnabled)
1522    {
1523      const bool enabled = getInTurtleTime (t);
1524      useAltSpeed (s, t, enabled, true);
1525      t->autoTurtleState = autoSwitchState (enabled);
1526    }
1527}
1528
1529void
1530tr_sessionUseAltSpeedTime (tr_session * s, bool b)
1531{
1532  struct tr_turtle_info * t = &s->turtle;
1533
1534  assert (tr_isSession (s));
1535  assert (tr_isBool (b));
1536
1537  if (t->isClockEnabled != b)
1538    {
1539      t->isClockEnabled = b;
1540      userPokedTheClock (s, t);
1541    }
1542}
1543
1544bool
1545tr_sessionUsesAltSpeedTime (const tr_session * s)
1546{
1547  assert (tr_isSession (s));
1548
1549  return s->turtle.isClockEnabled;
1550}
1551
1552void
1553tr_sessionSetAltSpeedBegin (tr_session * s, int minute)
1554{
1555  assert (tr_isSession (s));
1556  assert (0<=minute && minute< (60*24));
1557
1558  if (s->turtle.beginMinute != minute)
1559    {
1560      s->turtle.beginMinute = minute;
1561      userPokedTheClock (s, &s->turtle);
1562    }
1563}
1564
1565int
1566tr_sessionGetAltSpeedBegin (const tr_session * s)
1567{
1568  assert (tr_isSession (s));
1569
1570  return s->turtle.beginMinute;
1571}
1572
1573void
1574tr_sessionSetAltSpeedEnd (tr_session * s, int minute)
1575{
1576  assert (tr_isSession (s));
1577  assert (0<=minute && minute< (60*24));
1578
1579  if (s->turtle.endMinute != minute)
1580    {
1581      s->turtle.endMinute = minute;
1582      userPokedTheClock (s, &s->turtle);
1583    }
1584}
1585
1586int
1587tr_sessionGetAltSpeedEnd (const tr_session * s)
1588{
1589  assert (tr_isSession (s));
1590
1591  return s->turtle.endMinute;
1592}
1593
1594void
1595tr_sessionSetAltSpeedDay (tr_session * s, tr_sched_day days)
1596{
1597  assert (tr_isSession (s));
1598
1599  if (s->turtle.days != days)
1600    {
1601      s->turtle.days = days;
1602      userPokedTheClock (s, &s->turtle);
1603    }
1604}
1605
1606tr_sched_day
1607tr_sessionGetAltSpeedDay (const tr_session * s)
1608{
1609  assert (tr_isSession (s));
1610
1611  return s->turtle.days;
1612}
1613
1614void
1615tr_sessionUseAltSpeed (tr_session * session, bool enabled)
1616{
1617  useAltSpeed (session, &session->turtle, enabled, true);
1618}
1619
1620bool
1621tr_sessionUsesAltSpeed (const tr_session * s)
1622{
1623  assert (tr_isSession (s));
1624
1625  return s->turtle.isEnabled;
1626}
1627
1628void
1629tr_sessionSetAltSpeedFunc (tr_session       * session,
1630                           tr_altSpeedFunc    func,
1631                           void             * userData)
1632{
1633  assert (tr_isSession (session));
1634
1635  session->turtle.callback = func;
1636  session->turtle.callbackUserData = userData;
1637}
1638
1639void
1640tr_sessionClearAltSpeedFunc (tr_session * session)
1641{
1642  tr_sessionSetAltSpeedFunc (session, NULL, NULL);
1643}
1644
1645/***
1646****
1647***/
1648
1649void
1650tr_sessionSetPeerLimit (tr_session * session, uint16_t n)
1651{
1652  assert (tr_isSession (session));
1653
1654  session->peerLimit = n;
1655}
1656
1657uint16_t
1658tr_sessionGetPeerLimit (const tr_session * session)
1659{
1660  assert (tr_isSession (session));
1661
1662  return session->peerLimit;
1663}
1664
1665void
1666tr_sessionSetPeerLimitPerTorrent (tr_session  * session, uint16_t n)
1667{
1668    assert (tr_isSession (session));
1669
1670    session->peerLimitPerTorrent = n;
1671}
1672
1673uint16_t
1674tr_sessionGetPeerLimitPerTorrent (const tr_session * session)
1675{
1676  assert (tr_isSession (session));
1677
1678  return session->peerLimitPerTorrent;
1679}
1680
1681/***
1682****
1683***/
1684
1685void
1686tr_sessionSetPaused (tr_session * session, bool isPaused)
1687{
1688  assert (tr_isSession (session));
1689
1690  session->pauseAddedTorrent = isPaused;
1691}
1692
1693bool
1694tr_sessionGetPaused (const tr_session * session)
1695{
1696  assert (tr_isSession (session));
1697
1698  return session->pauseAddedTorrent;
1699}
1700
1701void
1702tr_sessionSetDeleteSource (tr_session * session, bool deleteSource)
1703{
1704  assert (tr_isSession (session));
1705
1706  session->deleteSourceTorrent = deleteSource;
1707}
1708
1709bool
1710tr_sessionGetDeleteSource (const tr_session * session)
1711{
1712  assert (tr_isSession (session));
1713
1714  return session->deleteSourceTorrent;
1715}
1716
1717/***
1718****
1719***/
1720
1721unsigned int
1722tr_sessionGetPieceSpeed_Bps (const tr_session * session, tr_direction dir)
1723{
1724  return tr_isSession (session) ? tr_bandwidthGetPieceSpeed_Bps (&session->bandwidth, 0, dir) : 0;
1725}
1726
1727unsigned int
1728tr_sessionGetRawSpeed_Bps (const tr_session * session, tr_direction dir)
1729{
1730  return tr_isSession (session) ? tr_bandwidthGetRawSpeed_Bps (&session->bandwidth, 0, dir) : 0;
1731}
1732double
1733tr_sessionGetRawSpeed_KBps (const tr_session * session, tr_direction dir)
1734{
1735  return toSpeedKBps (tr_sessionGetRawSpeed_Bps (session, dir));
1736}
1737
1738
1739int
1740tr_sessionCountTorrents (const tr_session * session)
1741{
1742  return tr_isSession (session) ? session->torrentCount : 0;
1743}
1744
1745tr_torrent **
1746tr_sessionGetTorrents (tr_session * session, int * setme_n)
1747{
1748  int i;
1749  int n;
1750  tr_torrent ** torrents;
1751  tr_torrent * tor;
1752
1753  assert (tr_isSession (session));
1754  assert (setme_n != NULL);
1755
1756  n = tr_sessionCountTorrents (session);
1757  *setme_n = n;
1758
1759  torrents = tr_new (tr_torrent *, n);
1760  tor = NULL;
1761  for (i=0; i<n; ++i)
1762    torrents[i] = tor = tr_torrentNext (session, tor);
1763
1764  return torrents;
1765} 
1766
1767static int
1768compareTorrentByCur (const void * va, const void * vb)
1769{
1770  const tr_torrent * a = * (const tr_torrent**)va;
1771  const tr_torrent * b = * (const tr_torrent**)vb;
1772  const uint64_t aCur = a->downloadedCur + a->uploadedCur;
1773  const uint64_t bCur = b->downloadedCur + b->uploadedCur;
1774
1775  if (aCur != bCur)
1776    return aCur > bCur ? -1 : 1; /* close the biggest torrents first */
1777
1778  return 0;
1779}
1780
1781static void closeBlocklists (tr_session *);
1782
1783static void
1784sessionCloseImpl (void * vsession)
1785{
1786  int i, n;
1787  tr_torrent ** torrents;
1788  tr_session * session = vsession;
1789
1790  assert (tr_isSession (session));
1791
1792  session->isClosing = true;
1793
1794  free_incoming_peer_port (session);
1795
1796  if (session->isLPDEnabled)
1797    tr_lpdUninit (session);
1798
1799  tr_utpClose (session);
1800  tr_dhtUninit (session);
1801
1802  event_free (session->saveTimer);
1803  session->saveTimer = NULL;
1804
1805  event_free (session->nowTimer);
1806  session->nowTimer = NULL;
1807
1808  tr_verifyClose (session);
1809  tr_sharedClose (session);
1810  tr_rpcClose (&session->rpcServer);
1811
1812  /* Close the torrents. Get the most active ones first so that
1813   * if we can't get them all closed in a reasonable amount of time,
1814   * at least we get the most important ones first. */
1815  torrents = tr_sessionGetTorrents (session, &n);
1816  qsort (torrents, n, sizeof (tr_torrent*), compareTorrentByCur);
1817  for (i=0; i<n; ++i)
1818    tr_torrentFree (torrents[i]);
1819  tr_free (torrents);
1820
1821  /* Close the announcer *after* closing the torrents
1822     so that all the &event=stopped messages will be
1823     queued to be sent by tr_announcerClose () */
1824  tr_announcerClose (session);
1825
1826  /* and this goes *after* announcer close so that
1827     it won't be idle until the announce events are sent... */
1828  tr_webClose (session, TR_WEB_CLOSE_WHEN_IDLE);
1829
1830  tr_cacheFree (session->cache);
1831  session->cache = NULL;
1832
1833  /* gotta keep udp running long enough to send out all
1834     the &event=stopped UDP tracker messages */
1835  while (!tr_tracker_udp_is_idle (session))
1836    {
1837      tr_tracker_udp_upkeep (session);
1838      tr_wait_msec (100);
1839    }
1840
1841  /* we had to wait until UDP trackers were closed before closing these: */
1842  evdns_base_free (session->evdns_base, 0);
1843  session->evdns_base = NULL;
1844  tr_tracker_udp_close (session);
1845  tr_udpUninit (session);
1846
1847  tr_statsClose (session);
1848  tr_peerMgrFree (session->peerMgr);
1849
1850  closeBlocklists (session);
1851
1852  tr_fdClose (session);
1853
1854  session->isClosed = true;
1855}
1856
1857static int
1858deadlineReached (const time_t deadline)
1859{
1860  return time (NULL) >= deadline;
1861}
1862
1863#define SHUTDOWN_MAX_SECONDS 20
1864
1865void
1866tr_sessionClose (tr_session * session)
1867{
1868  const time_t deadline = time (NULL) + SHUTDOWN_MAX_SECONDS;
1869
1870  assert (tr_isSession (session));
1871
1872  dbgmsg ("shutting down transmission session %p... now is %"TR_PRIuSIZE", deadline is %"TR_PRIuSIZE, (void*)session, (size_t)time (NULL), (size_t)deadline);
1873
1874  /* close the session */
1875  tr_runInEventThread (session, sessionCloseImpl, session);
1876  while (!session->isClosed && !deadlineReached (deadline))
1877    {
1878      dbgmsg ("waiting for the libtransmission thread to finish");
1879      tr_wait_msec (100);
1880    }
1881
1882  /* "shared" and "tracker" have live sockets,
1883   * so we need to keep the transmission thread alive
1884   * for a bit while they tell the router & tracker
1885   * that we're closing now */
1886  while ((session->shared || session->web || session->announcer || session->announcer_udp)
1887           && !deadlineReached (deadline))
1888    {
1889      dbgmsg ("waiting on port unmap (%p) or announcer (%p)... now %"TR_PRIuSIZE" deadline %"TR_PRIuSIZE,
1890              (void*)session->shared, (void*)session->announcer, (size_t)time (NULL), (size_t)deadline);
1891      tr_wait_msec (50);
1892    }
1893
1894  tr_webClose (session, TR_WEB_CLOSE_NOW);
1895
1896  /* close the libtransmission thread */
1897  tr_eventClose (session);
1898  while (session->events != NULL)
1899    {
1900      static bool forced = false;
1901      dbgmsg ("waiting for libtransmission thread to finish... now %"TR_PRIuSIZE" deadline %"TR_PRIuSIZE, (size_t)time (NULL), (size_t)deadline);
1902      tr_wait_msec (100);
1903
1904      if (deadlineReached (deadline) && !forced)
1905        {
1906          dbgmsg ("calling event_loopbreak ()");
1907          forced = true;
1908          event_base_loopbreak (session->event_base);
1909        }
1910
1911      if (deadlineReached (deadline+3))
1912        {
1913          dbgmsg ("deadline+3 reached... calling break...\n");
1914          break;
1915        }
1916    }
1917
1918  /* free the session memory */
1919  tr_variantFree (&session->removedTorrents);
1920  tr_bandwidthDestruct (&session->bandwidth);
1921  tr_bitfieldDestruct (&session->turtle.minutes);
1922  tr_lockFree (session->lock);
1923  if (session->metainfoLookup)
1924    {
1925      tr_variantFree (session->metainfoLookup);
1926      tr_free (session->metainfoLookup);
1927    }
1928  tr_device_info_free (session->downloadDir);
1929  tr_free (session->torrentDoneScript);
1930  tr_free (session->tag);
1931  tr_free (session->configDir);
1932  tr_free (session->resumeDir);
1933  tr_free (session->torrentDir);
1934  tr_free (session->incompleteDir);
1935  tr_free (session->blocklist_url);
1936  tr_free (session->peer_congestion_algorithm);
1937  tr_free (session);
1938}
1939
1940struct sessionLoadTorrentsData
1941{
1942  tr_session * session;
1943  tr_ctor * ctor;
1944  int * setmeCount;
1945  tr_torrent ** torrents;
1946  bool done;
1947};
1948
1949static void
1950sessionLoadTorrents (void * vdata)
1951{
1952  int i;
1953  int n = 0;
1954  tr_sys_path_info info;
1955  tr_sys_dir_t odir = NULL;
1956  tr_list * l = NULL;
1957  tr_list * list = NULL;
1958  struct sessionLoadTorrentsData * data = vdata;
1959  const char * dirname = tr_getTorrentDir (data->session);
1960
1961  assert (tr_isSession (data->session));
1962
1963  tr_ctorSetSave (data->ctor, false); /* since we already have them */
1964
1965  if (tr_sys_path_get_info (dirname, 0, &info, NULL) &&
1966      info.type == TR_SYS_PATH_IS_DIRECTORY &&
1967      (odir = tr_sys_dir_open (dirname, NULL)) != TR_BAD_SYS_DIR)
1968    {
1969      const char * name;
1970      while ((name = tr_sys_dir_read_name (odir, NULL)) != NULL)
1971        {
1972          if (tr_str_has_suffix (name, ".torrent"))
1973            {
1974              tr_torrent * tor;
1975              char * path = tr_buildPath (dirname, name, NULL);
1976              tr_ctorSetMetainfoFromFile (data->ctor, path);
1977              if ((tor = tr_torrentNew (data->ctor, NULL, NULL)))
1978                {
1979                  tr_list_prepend (&list, tor);
1980                  ++n;
1981                }
1982              tr_free (path);
1983            }
1984        }
1985      tr_sys_dir_close (odir, NULL);
1986    }
1987
1988  data->torrents = tr_new (tr_torrent *, n);
1989  for (i=0, l=list; l!=NULL; l=l->next)
1990    data->torrents[i++] = (tr_torrent*) l->data;
1991  assert (i == n);
1992
1993    tr_list_free (&list, NULL);
1994
1995  if (n)
1996    tr_logAddInfo (_("Loaded %d torrents"), n);
1997
1998  if (data->setmeCount)
1999    *data->setmeCount = n;
2000
2001  data->done = true;
2002}
2003
2004tr_torrent **
2005tr_sessionLoadTorrents (tr_session * session,
2006                        tr_ctor    * ctor,
2007                        int        * setmeCount)
2008{
2009  struct sessionLoadTorrentsData data;
2010
2011  data.session = session;
2012  data.ctor = ctor;
2013  data.setmeCount = setmeCount;
2014  data.torrents = NULL;
2015  data.done = false;
2016
2017  tr_runInEventThread (session, sessionLoadTorrents, &data);
2018  while (!data.done)
2019    tr_wait_msec (100);
2020
2021  return data.torrents;
2022}
2023
2024/***
2025****
2026***/
2027
2028void
2029tr_sessionSetPexEnabled (tr_session * session, bool enabled)
2030{
2031  assert (tr_isSession (session));
2032  assert (tr_isBool (enabled));
2033
2034  session->isPexEnabled = enabled;
2035}
2036
2037bool
2038tr_sessionIsPexEnabled (const tr_session * session)
2039{
2040  assert (tr_isSession (session));
2041
2042  return session->isPexEnabled;
2043}
2044
2045bool
2046tr_sessionAllowsDHT (const tr_session * session)
2047{
2048  return tr_sessionIsDHTEnabled (session);
2049}
2050
2051bool
2052tr_sessionIsDHTEnabled (const tr_session * session)
2053{
2054  assert (tr_isSession (session));
2055
2056  return session->isDHTEnabled;
2057}
2058
2059static void
2060toggleDHTImpl (void * data)
2061{
2062  tr_session * session = data;
2063  assert (tr_isSession (session));
2064
2065  tr_udpUninit (session);
2066  session->isDHTEnabled = !session->isDHTEnabled;
2067  tr_udpInit (session);
2068}
2069
2070void
2071tr_sessionSetDHTEnabled (tr_session * session, bool enabled)
2072{
2073  assert (tr_isSession (session));
2074  assert (tr_isBool (enabled));
2075
2076  if (enabled != session->isDHTEnabled)
2077    tr_runInEventThread (session, toggleDHTImpl, session);
2078}
2079
2080/***
2081****
2082***/
2083
2084bool
2085tr_sessionIsUTPEnabled (const tr_session * session)
2086{
2087  assert (tr_isSession (session));
2088
2089#ifdef WITH_UTP
2090  return session->isUTPEnabled;
2091#else
2092  return false;
2093#endif
2094}
2095
2096static void
2097toggle_utp (void * data)
2098{
2099  tr_session * session = data;
2100
2101  assert (tr_isSession (session));
2102
2103  session->isUTPEnabled = !session->isUTPEnabled;
2104
2105  tr_udpSetSocketBuffers (session);
2106
2107  /* But don't call tr_utpClose -- see reset_timer in tr-utp.c for an
2108     explanation. */
2109}
2110
2111void
2112tr_sessionSetUTPEnabled (tr_session * session, bool enabled)
2113{
2114  assert (tr_isSession (session));
2115  assert (tr_isBool (enabled));
2116
2117  if (enabled != session->isUTPEnabled)
2118    tr_runInEventThread (session, toggle_utp, session);
2119}
2120
2121/***
2122****
2123***/
2124
2125static void
2126toggleLPDImpl (void * data)
2127{
2128  tr_session * session = data;
2129  assert (tr_isSession (session));
2130
2131  if (session->isLPDEnabled)
2132    tr_lpdUninit (session);
2133
2134  session->isLPDEnabled = !session->isLPDEnabled;
2135
2136  if (session->isLPDEnabled)
2137    tr_lpdInit (session, &session->public_ipv4->addr);
2138}
2139
2140void
2141tr_sessionSetLPDEnabled (tr_session * session, bool enabled)
2142{
2143  assert (tr_isSession (session));
2144  assert (tr_isBool (enabled));
2145
2146  if (enabled != session->isLPDEnabled)
2147    tr_runInEventThread (session, toggleLPDImpl, session);
2148}
2149
2150bool
2151tr_sessionIsLPDEnabled (const tr_session * session)
2152{
2153  assert (tr_isSession (session));
2154
2155  return session->isLPDEnabled;
2156}
2157
2158bool
2159tr_sessionAllowsLPD (const tr_session * session)
2160{
2161  return tr_sessionIsLPDEnabled (session);
2162}
2163
2164/***
2165****
2166***/
2167
2168void
2169tr_sessionSetCacheLimit_MB (tr_session * session, int max_bytes)
2170{
2171  assert (tr_isSession (session));
2172
2173  tr_cacheSetLimit (session->cache, toMemBytes (max_bytes));
2174}
2175
2176int
2177tr_sessionGetCacheLimit_MB (const tr_session * session)
2178{
2179  assert (tr_isSession (session));
2180
2181  return toMemMB (tr_cacheGetLimit (session->cache));
2182}
2183
2184/***
2185****
2186***/
2187
2188struct port_forwarding_data
2189{
2190  bool enabled;
2191  struct tr_shared * shared;
2192};
2193
2194static void
2195setPortForwardingEnabled (void * vdata)
2196{
2197  struct port_forwarding_data * data = vdata;
2198  tr_sharedTraversalEnable (data->shared, data->enabled);
2199  tr_free (data);
2200}
2201
2202void
2203tr_sessionSetPortForwardingEnabled (tr_session  * session, bool enabled)
2204{
2205  struct port_forwarding_data * d;
2206  d = tr_new0 (struct port_forwarding_data, 1);
2207  d->shared = session->shared;
2208  d->enabled = enabled;
2209  tr_runInEventThread (session, setPortForwardingEnabled, d);
2210}
2211
2212bool
2213tr_sessionIsPortForwardingEnabled (const tr_session * session)
2214{
2215  assert (tr_isSession (session));
2216
2217  return tr_sharedTraversalIsEnabled (session->shared);
2218}
2219
2220/***
2221****
2222***/
2223
2224static int
2225tr_stringEndsWith (const char * str, const char * end)
2226{
2227  const size_t slen = strlen (str);
2228  const size_t elen = strlen (end);
2229
2230  return slen >= elen && !memcmp (&str[slen - elen], end, elen);
2231}
2232
2233static void
2234loadBlocklists (tr_session * session)
2235{
2236  tr_sys_dir_t odir;
2237  char * dirname;
2238  const char * name;
2239  tr_list * blocklists = NULL;
2240  tr_ptrArray loadme = TR_PTR_ARRAY_INIT;
2241  const bool isEnabled = session->isBlocklistEnabled;
2242
2243  /* walk the blocklist directory... */
2244  dirname = tr_buildPath (session->configDir, "blocklists", NULL);
2245  odir = tr_sys_dir_open (dirname, NULL);
2246  if (odir == TR_BAD_SYS_DIR)
2247    {
2248      tr_free (dirname);
2249      return;
2250    }
2251
2252  while ((name = tr_sys_dir_read_name (odir, NULL)) != NULL)
2253    {
2254      char * path;
2255      char * load = NULL;
2256 
2257      if (name[0] == '.') /* ignore dotfiles */
2258        continue;
2259
2260      path = tr_buildPath (dirname, name, NULL);
2261
2262      if (tr_stringEndsWith (path, ".bin"))
2263        {
2264          load = tr_strdup (path);
2265        }
2266      else
2267        {
2268          char * binname;
2269          char * basename;
2270          tr_sys_path_info path_info;
2271          tr_sys_path_info binname_info;
2272
2273          basename = tr_sys_path_basename (name, NULL);
2274          binname = tr_strdup_printf ("%s" TR_PATH_DELIMITER_STR "%s.bin", dirname, basename);
2275
2276          if (!tr_sys_path_get_info (binname, 0, &binname_info, NULL)) /* create it */
2277            {
2278              tr_blocklistFile * b = tr_blocklistFileNew (binname, isEnabled);
2279              const int n = tr_blocklistFileSetContent (b, path);
2280              if (n > 0)
2281                load = tr_strdup (binname);
2282
2283              tr_blocklistFileFree (b);
2284            }
2285          else if (tr_sys_path_get_info (path, 0, &path_info, NULL) &&
2286                   path_info.last_modified_at >= binname_info.last_modified_at) /* update it */
2287            {
2288              char * old;
2289              tr_blocklistFile * b;
2290
2291              old = tr_strdup_printf ("%s.old", binname);
2292              tr_sys_path_remove (old, NULL);
2293              tr_sys_path_rename (binname, old, NULL);
2294              b = tr_blocklistFileNew (binname, isEnabled);
2295              if (tr_blocklistFileSetContent (b, path) > 0)
2296                {
2297                  tr_sys_path_remove (old, NULL);
2298                }
2299              else
2300                {
2301                  tr_sys_path_remove (binname, NULL);
2302                  tr_sys_path_rename (old, binname, NULL);
2303                }
2304
2305              tr_blocklistFileFree (b);
2306              tr_free (old);
2307            }
2308
2309          tr_free (basename);
2310          tr_free (binname);
2311        }
2312
2313      if (load != NULL)
2314        {
2315          if (tr_ptrArrayFindSorted (&loadme, load, (PtrArrayCompareFunc)strcmp) == NULL)
2316            tr_ptrArrayInsertSorted (&loadme, load, (PtrArrayCompareFunc)strcmp);
2317          else
2318            tr_free (load);
2319        }
2320
2321      tr_free (path);
2322    }
2323
2324  if (!tr_ptrArrayEmpty (&loadme))
2325    {
2326      int i;
2327      const int n = tr_ptrArraySize (&loadme);
2328      const char ** paths = (const char **) tr_ptrArrayBase (&loadme);
2329
2330      for (i=0; i<n; ++i)
2331        tr_list_append (&blocklists, tr_blocklistFileNew (paths[i], isEnabled));
2332    }
2333
2334  /* cleanup */
2335  tr_sys_dir_close (odir, NULL);
2336  tr_free (dirname);
2337  tr_ptrArrayDestruct (&loadme, (PtrArrayForeachFunc)tr_free);
2338  session->blocklists = blocklists;
2339}
2340
2341static void
2342closeBlocklists (tr_session * session)
2343{
2344  tr_list_free (&session->blocklists, (TrListForeachFunc)tr_blocklistFileFree);
2345}
2346
2347void
2348tr_sessionReloadBlocklists (tr_session * session)
2349{
2350  closeBlocklists (session);
2351  loadBlocklists (session);
2352
2353  tr_peerMgrOnBlocklistChanged (session->peerMgr);
2354}
2355
2356int
2357tr_blocklistGetRuleCount (const tr_session * session)
2358{
2359  tr_list * l;
2360  int n = 0;
2361
2362  assert (tr_isSession (session));
2363
2364  for (l = session->blocklists; l; l = l->next)
2365    n += tr_blocklistFileGetRuleCount (l->data);
2366
2367  return n;
2368}
2369
2370bool
2371tr_blocklistIsEnabled (const tr_session * session)
2372{
2373  assert (tr_isSession (session));
2374
2375  return session->isBlocklistEnabled;
2376}
2377
2378void
2379tr_blocklistSetEnabled (tr_session * session, bool isEnabled)
2380{
2381  tr_list * l;
2382
2383  assert (tr_isSession (session));
2384  assert (tr_isBool (isEnabled));
2385
2386  session->isBlocklistEnabled = isEnabled;
2387
2388  for (l=session->blocklists; l!=NULL; l=l->next)
2389    tr_blocklistFileSetEnabled (l->data, isEnabled);
2390}
2391
2392bool
2393tr_blocklistExists (const tr_session * session)
2394{
2395  assert (tr_isSession (session));
2396
2397  return session->blocklists != NULL;
2398}
2399
2400int
2401tr_blocklistSetContent (tr_session * session, const char * contentFilename)
2402{
2403  tr_list * l;
2404  int ruleCount;
2405  tr_blocklistFile * b;
2406  const char * defaultName = DEFAULT_BLOCKLIST_FILENAME;
2407  tr_sessionLock (session);
2408
2409  for (b=NULL, l=session->blocklists; !b && l; l=l->next)
2410    if (tr_stringEndsWith (tr_blocklistFileGetFilename (l->data), defaultName))
2411      b = l->data;
2412
2413  if (!b)
2414    {
2415      char * path = tr_buildPath (session->configDir, "blocklists", defaultName, NULL);
2416      b = tr_blocklistFileNew (path, session->isBlocklistEnabled);
2417      tr_list_append (&session->blocklists, b);
2418      tr_free (path);
2419    }
2420
2421  ruleCount = tr_blocklistFileSetContent (b, contentFilename);
2422  tr_sessionUnlock (session);
2423  return ruleCount;
2424}
2425
2426bool
2427tr_sessionIsAddressBlocked (const tr_session * session,
2428                            const tr_address * addr)
2429{
2430  tr_list * l;
2431
2432  assert (tr_isSession (session));
2433
2434  for (l = session->blocklists; l; l = l->next)
2435    if (tr_blocklistFileHasAddress (l->data, addr))
2436      return true;
2437
2438  return false;
2439}
2440
2441void
2442tr_blocklistSetURL (tr_session * session, const char * url)
2443{
2444  if (session->blocklist_url != url)
2445    {
2446      tr_free (session->blocklist_url);
2447      session->blocklist_url = tr_strdup (url);
2448    }
2449}
2450
2451const char *
2452tr_blocklistGetURL (const tr_session * session)
2453{
2454  return session->blocklist_url;
2455}
2456
2457
2458/***
2459****
2460***/
2461
2462static void
2463metainfoLookupInit (tr_session * session)
2464{
2465  tr_sys_path_info info;
2466  const char * dirname = tr_getTorrentDir (session);
2467  tr_sys_dir_t odir;
2468  tr_ctor * ctor = NULL;
2469  tr_variant * lookup;
2470  int n = 0;
2471
2472  assert (tr_isSession (session));
2473
2474  /* walk through the directory and find the mappings */
2475  lookup = tr_new0 (tr_variant, 1);
2476  tr_variantInitDict (lookup, 0);
2477  ctor = tr_ctorNew (session);
2478  tr_ctorSetSave (ctor, false); /* since we already have them */
2479  if (tr_sys_path_get_info (dirname, 0, &info, NULL) &&
2480      info.type == TR_SYS_PATH_IS_DIRECTORY &&
2481      (odir = tr_sys_dir_open (dirname, NULL)) != TR_BAD_SYS_DIR)
2482    {
2483      const char * name;
2484      while ((name = tr_sys_dir_read_name (odir, NULL)) != NULL)
2485        {
2486          if (tr_str_has_suffix (name, ".torrent"))
2487            {
2488              tr_info inf;
2489              char * path = tr_buildPath (dirname, name, NULL);
2490              tr_ctorSetMetainfoFromFile (ctor, path);
2491              if (!tr_torrentParse (ctor, &inf))
2492                {
2493                  ++n;
2494                  tr_variantDictAddStr (lookup, tr_quark_new(inf.hashString,-1), path);
2495                }
2496              tr_free (path);
2497            }
2498        }
2499      tr_sys_dir_close (odir, NULL);
2500    }
2501  tr_ctorFree (ctor);
2502
2503  session->metainfoLookup = lookup;
2504  tr_logAddDebug ("Found %d torrents in \"%s\"", n, dirname);
2505}
2506
2507const char*
2508tr_sessionFindTorrentFile (const tr_session * session,
2509                           const char       * hashString)
2510{
2511  const char * filename = NULL;
2512
2513  if (!session->metainfoLookup)
2514    metainfoLookupInit ((tr_session*)session);
2515  tr_variantDictFindStr (session->metainfoLookup, tr_quark_new(hashString,-1), &filename, NULL);
2516
2517  return filename;
2518}
2519
2520void
2521tr_sessionSetTorrentFile (tr_session * session,
2522                          const char * hashString,
2523                          const char * filename)
2524{
2525  /* since we walk session->configDir/torrents/ to build the lookup table,
2526   * and tr_sessionSetTorrentFile () is just to tell us there's a new file
2527   * in that same directory, we don't need to do anything here if the
2528   * lookup table hasn't been built yet */
2529  if (session->metainfoLookup)
2530    tr_variantDictAddStr (session->metainfoLookup, tr_quark_new(hashString,-1), filename);
2531}
2532
2533/***
2534****
2535***/
2536
2537void
2538tr_sessionSetRPCEnabled (tr_session * session, bool isEnabled)
2539{
2540  assert (tr_isSession (session));
2541
2542  tr_rpcSetEnabled (session->rpcServer, isEnabled);
2543}
2544
2545bool
2546tr_sessionIsRPCEnabled (const tr_session * session)
2547{
2548  assert (tr_isSession (session));
2549
2550  return tr_rpcIsEnabled (session->rpcServer);
2551}
2552
2553void
2554tr_sessionSetRPCPort (tr_session * session,
2555                      tr_port      port)
2556{
2557  assert (tr_isSession (session));
2558
2559  tr_rpcSetPort (session->rpcServer, port);
2560}
2561
2562tr_port
2563tr_sessionGetRPCPort (const tr_session * session)
2564{
2565  assert (tr_isSession (session));
2566
2567  return tr_rpcGetPort (session->rpcServer);
2568}
2569
2570void
2571tr_sessionSetRPCUrl (tr_session * session,
2572                     const char * url)
2573{
2574  assert (tr_isSession (session));
2575
2576  tr_rpcSetUrl (session->rpcServer, url);
2577}
2578
2579const char*
2580tr_sessionGetRPCUrl (const tr_session * session)
2581{
2582  assert (tr_isSession (session));
2583
2584  return tr_rpcGetUrl (session->rpcServer);
2585}
2586
2587void
2588tr_sessionSetRPCCallback (tr_session * session,
2589                          tr_rpc_func  func,
2590                          void *       user_data)
2591{
2592  assert (tr_isSession (session));
2593
2594  session->rpc_func = func;
2595  session->rpc_func_user_data = user_data;
2596}
2597
2598void
2599tr_sessionSetRPCWhitelist (tr_session * session,
2600                           const char * whitelist)
2601{
2602  assert (tr_isSession (session));
2603
2604  tr_rpcSetWhitelist (session->rpcServer, whitelist);
2605}
2606
2607const char*
2608tr_sessionGetRPCWhitelist (const tr_session * session)
2609{
2610  assert (tr_isSession (session));
2611
2612  return tr_rpcGetWhitelist (session->rpcServer);
2613}
2614
2615void
2616tr_sessionSetRPCWhitelistEnabled (tr_session * session, bool isEnabled)
2617{
2618  assert (tr_isSession (session));
2619
2620  tr_rpcSetWhitelistEnabled (session->rpcServer, isEnabled);
2621}
2622
2623bool
2624tr_sessionGetRPCWhitelistEnabled (const tr_session * session)
2625{
2626  assert (tr_isSession (session));
2627
2628  return tr_rpcGetWhitelistEnabled (session->rpcServer);
2629}
2630
2631
2632void
2633tr_sessionSetRPCPassword (tr_session * session,
2634                          const char * password)
2635{
2636  assert (tr_isSession (session));
2637
2638  tr_rpcSetPassword (session->rpcServer, password);
2639}
2640
2641const char*
2642tr_sessionGetRPCPassword (const tr_session * session)
2643{
2644  assert (tr_isSession (session));
2645
2646  return tr_rpcGetPassword (session->rpcServer);
2647}
2648
2649void
2650tr_sessionSetRPCUsername (tr_session * session,
2651                          const char * username)
2652{
2653  assert (tr_isSession (session));
2654
2655  tr_rpcSetUsername (session->rpcServer, username);
2656}
2657
2658const char*
2659tr_sessionGetRPCUsername (const tr_session * session)
2660{
2661  assert (tr_isSession (session));
2662
2663  return tr_rpcGetUsername (session->rpcServer);
2664}
2665
2666void
2667tr_sessionSetRPCPasswordEnabled (tr_session * session, bool isEnabled)
2668{
2669  assert (tr_isSession (session));
2670
2671  tr_rpcSetPasswordEnabled (session->rpcServer, isEnabled);
2672}
2673
2674bool
2675tr_sessionIsRPCPasswordEnabled (const tr_session * session)
2676{
2677  assert (tr_isSession (session));
2678
2679  return tr_rpcIsPasswordEnabled (session->rpcServer);
2680}
2681
2682const char *
2683tr_sessionGetRPCBindAddress (const tr_session * session)
2684{
2685  assert (tr_isSession (session));
2686
2687  return tr_rpcGetBindAddress (session->rpcServer);
2688}
2689
2690/****
2691*****
2692****/
2693
2694bool
2695tr_sessionIsTorrentDoneScriptEnabled (const tr_session * session)
2696{
2697  assert (tr_isSession (session));
2698
2699  return session->isTorrentDoneScriptEnabled;
2700}
2701
2702void
2703tr_sessionSetTorrentDoneScriptEnabled (tr_session * session, bool isEnabled)
2704{
2705  assert (tr_isSession (session));
2706  assert (tr_isBool (isEnabled));
2707
2708  session->isTorrentDoneScriptEnabled = isEnabled;
2709}
2710
2711const char *
2712tr_sessionGetTorrentDoneScript (const tr_session * session)
2713{
2714  assert (tr_isSession (session));
2715
2716  return session->torrentDoneScript;
2717}
2718
2719void
2720tr_sessionSetTorrentDoneScript (tr_session * session, const char * scriptFilename)
2721{
2722  assert (tr_isSession (session));
2723
2724  if (session->torrentDoneScript != scriptFilename)
2725    {
2726      tr_free (session->torrentDoneScript);
2727      session->torrentDoneScript = tr_strdup (scriptFilename);
2728    }
2729}
2730
2731/***
2732****
2733***/
2734
2735void
2736tr_sessionSetQueueSize (tr_session * session, tr_direction dir, int n)
2737{
2738  assert (tr_isSession (session));
2739  assert (tr_isDirection (dir));
2740
2741  session->queueSize[dir] = n;
2742}
2743
2744int
2745tr_sessionGetQueueSize (const tr_session * session, tr_direction dir)
2746{
2747  assert (tr_isSession (session));
2748  assert (tr_isDirection (dir));
2749
2750  return session->queueSize[dir];
2751}
2752
2753void
2754tr_sessionSetQueueEnabled (tr_session * session, tr_direction dir, bool is_enabled)
2755{
2756  assert (tr_isSession (session));
2757  assert (tr_isDirection (dir));
2758  assert (tr_isBool (is_enabled));
2759
2760  session->queueEnabled[dir] = is_enabled;
2761}
2762
2763bool
2764tr_sessionGetQueueEnabled (const tr_session * session, tr_direction dir)
2765{
2766  assert (tr_isSession (session));
2767  assert (tr_isDirection (dir));
2768
2769  return session->queueEnabled[dir];
2770}
2771
2772void
2773tr_sessionSetQueueStalledMinutes (tr_session * session, int minutes)
2774{
2775  assert (tr_isSession (session));
2776  assert (minutes > 0);
2777
2778  session->queueStalledMinutes = minutes;
2779}
2780
2781void
2782tr_sessionSetQueueStalledEnabled (tr_session * session, bool is_enabled)
2783{
2784  assert (tr_isSession (session));
2785  assert (tr_isBool (is_enabled));
2786
2787  session->stalledEnabled = is_enabled;
2788}
2789
2790bool
2791tr_sessionGetQueueStalledEnabled (const tr_session * session)
2792{
2793  assert (tr_isSession (session));
2794
2795  return session->stalledEnabled;
2796}
2797
2798int
2799tr_sessionGetQueueStalledMinutes (const tr_session * session)
2800{
2801  assert (tr_isSession (session));
2802
2803  return session->queueStalledMinutes;
2804}
2805
2806struct TorrentAndPosition
2807{
2808  tr_torrent * tor;
2809  int position;
2810};
2811
2812static int
2813compareTorrentAndPositions (const void * va, const void * vb)
2814{
2815  int ret;
2816  const struct TorrentAndPosition * a = va;
2817  const struct TorrentAndPosition * b = vb;
2818
2819  if (a->position > b->position)
2820    ret = 1;
2821  else if (a->position < b->position)
2822    ret = -1;
2823  else
2824    ret = 0;
2825
2826  return ret;
2827}
2828
2829
2830void
2831tr_sessionGetNextQueuedTorrents (tr_session   * session,
2832                                 tr_direction   direction,
2833                                 size_t         num_wanted,
2834                                 tr_ptrArray  * setme)
2835{
2836  size_t i;
2837  size_t n;
2838  tr_torrent * tor;
2839  struct TorrentAndPosition * candidates;
2840
2841  assert (tr_isSession (session));
2842  assert (tr_isDirection (direction));
2843
2844  /* build an array of the candidates */
2845  n = tr_sessionCountTorrents (session);
2846  candidates = tr_new (struct TorrentAndPosition, n);
2847  i = 0;
2848  tor = NULL;
2849  while ((tor = tr_torrentNext (session, tor)))
2850    {
2851      if (!tr_torrentIsQueued (tor))
2852        continue;
2853
2854      if (direction != tr_torrentGetQueueDirection (tor))
2855        continue;
2856
2857      candidates[i].tor = tor;
2858      candidates[i].position = tr_torrentGetQueuePosition (tor);
2859      ++i;
2860    }
2861
2862  /* find the best n candidates */
2863  if (num_wanted > i)
2864    num_wanted = i;
2865  else if (num_wanted < i)
2866    tr_quickfindFirstK (candidates, i,
2867                        sizeof(struct TorrentAndPosition),
2868                        compareTorrentAndPositions, num_wanted);
2869
2870  /* add them to the return array */
2871  for (i=0; i<num_wanted; ++i)
2872    tr_ptrArrayAppend (setme, candidates[i].tor);
2873
2874  /* cleanup */
2875  tr_free (candidates);
2876}
2877
2878int
2879tr_sessionCountQueueFreeSlots (tr_session * session, tr_direction dir)
2880{
2881  tr_torrent * tor;
2882  int active_count;
2883  const int max = tr_sessionGetQueueSize (session, dir);
2884  const tr_torrent_activity activity = dir == TR_UP ? TR_STATUS_SEED : TR_STATUS_DOWNLOAD;
2885
2886  if (!tr_sessionGetQueueEnabled (session, dir))
2887    return INT_MAX;
2888
2889    tor = NULL;
2890    active_count = 0;
2891    while ((tor = tr_torrentNext (session, tor)))
2892        if (!tr_torrentIsStalled (tor))
2893            if (tr_torrentGetActivity (tor) == activity)
2894                ++active_count;
2895
2896    if (active_count >= max)
2897        return 0;
2898
2899    return max - active_count;
2900}
Note: See TracBrowser for help on using the repository browser.