source: trunk/libtransmission/session.c @ 13847

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

(libT) replace errant snprintf() calls with tr_snprintf() calls

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