source: trunk/libtransmission/session.c @ 13329

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

(trunk libT) fix the Linux build wrt compiling with the new snapshot of libutp checked into r13317

Previously we made sure to include stdbool.h (via transmission.h) before utp.h, since the latter used 'bool' without defining it. The new snapshot defines it unconditionally in non-C++ code, so now we need to include it first.

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