source: trunk/libtransmission/session.c @ 14130

Last change on this file since 14130 was 14130, checked in by jordan, 9 years ago

add tr_sessionGetTorrents(), a private utility to avoid code duplication in libtransmission

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