source: trunk/libtransmission/session.c @ 14526

Last change on this file since 14526 was 14526, checked in by mikedld, 6 years ago

Fix some issues revealed by coverity

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