source: trunk/libtransmission/session.c @ 14581

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

Remove unused session tag

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