source: trunk/libtransmission/session.c @ 12649

Last change on this file since 12649 was 12649, checked in by jordan, 10 years ago

queuing: lower the default 'active downloads' value to 5

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