source: trunk/libtransmission/session.c @ 14354

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

#4400, #5462: Move random helpers to crypto-utils

On a way to factoring out OpenSSL support to a standalone file to ease
addition of other crypto libraries support in the future, move helpers
providing random numbers/data generation to crypto-utils.{c,h}. OpenSSL-
related functionality (generation of cryptographically strong random
data) is moved to crypto-utils-openssl.c.

Rename functions to follow currently accepted style:

  • tr_cryptoRandBuf -> tr_rand_buffer
  • tr_cryptoRandInt -> tr_rand_int
  • tr_cryptoWeakRandInt -> tr_rand_int_weak

Fix rare case of invalid value being returned from tr_rand_int. Return
value for abs(INT_MIN) may be undefined and thus negative, and so
tr_rand_int will return negative value which is incorrect (out of
requested and expected range).

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