source: trunk/libtransmission/session.c

Last change on this file was 14718, checked in by mikedld, 5 years ago

Explicitly compare result of str(n)cmp/memcmp to signify that it's not boolean

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