source: trunk/libtransmission/session.c @ 13501

Last change on this file since 13501 was 13501, checked in by livings124, 10 years ago

return a value (and rename) testTurtleTime instead of passing in a flag to set

  • Property svn:keywords set to Date Rev Author Id
File size: 77.6 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 13501 2012-09-18 03:44:59Z livings124 $
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, unsigned 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    unsigned 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    unsigned 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 * return whether turtle should be on/off according to the scheduler
1325 */
1326static bool
1327getInTurtleTime( const struct tr_turtle_info * t )
1328{
1329    struct tm tm;
1330    size_t minute_of_the_week;
1331    const time_t now = tr_time( );
1332
1333    tr_localtime_r( &now, &tm );
1334
1335    minute_of_the_week = tm.tm_wday * MINUTES_PER_DAY
1336                       + tm.tm_hour * MINUTES_PER_HOUR
1337                       + tm.tm_min;
1338    if( minute_of_the_week >= MINUTES_PER_WEEK ) /* leap minutes? */
1339        minute_of_the_week = MINUTES_PER_WEEK - 1;
1340
1341    return tr_bitfieldHas( &t->minutes, minute_of_the_week );
1342}
1343
1344static inline tr_auto_switch_state_t
1345autoSwitchState( bool enabled )
1346{
1347    return enabled ? TR_AUTO_SWITCH_ON : TR_AUTO_SWITCH_OFF;
1348}
1349
1350static void
1351turtleCheckClock( tr_session * s, struct tr_turtle_info * t )
1352{
1353    bool enabled;
1354    bool alreadySwitched;
1355    tr_auto_switch_state_t newAutoTurtleState;
1356
1357    assert( t->isClockEnabled );
1358
1359    enabled = getInTurtleTime( t );
1360    newAutoTurtleState = autoSwitchState( enabled );
1361    alreadySwitched = ( t->autoTurtleState == newAutoTurtleState );
1362
1363    if( !alreadySwitched )
1364    {
1365        tr_inf( "Time to turn %s turtle mode!", (enabled?"on":"off") );
1366        t->autoTurtleState = newAutoTurtleState;
1367        useAltSpeed( s, t, enabled, false );
1368    }
1369}
1370
1371/* Called after the turtle's fields are loaded from an outside source.
1372 * It initializes the implementation fields
1373 * and turns on turtle mode if the clock settings say to. */
1374static void
1375turtleBootstrap( tr_session * session, struct tr_turtle_info * turtle )
1376{
1377    turtle->changedByUser = false;
1378    turtle->autoTurtleState = TR_AUTO_SWITCH_UNUSED;
1379
1380    tr_bitfieldConstruct( &turtle->minutes, MINUTES_PER_WEEK );
1381
1382    turtleUpdateTable( turtle );
1383
1384    if( turtle->isClockEnabled )
1385    {
1386        turtle->isEnabled = getInTurtleTime( turtle );
1387        turtle->autoTurtleState = autoSwitchState( turtle->isEnabled );
1388    }
1389
1390    altSpeedToggled( session );
1391
1392}
1393
1394/***
1395****  Primary session speed limits
1396***/
1397
1398void
1399tr_sessionSetSpeedLimit_Bps( tr_session * s, tr_direction d, unsigned int Bps )
1400{
1401    assert( tr_isSession( s ) );
1402    assert( tr_isDirection( d ) );
1403
1404    s->speedLimit_Bps[d] = Bps;
1405
1406    updateBandwidth( s, d );
1407}
1408void
1409tr_sessionSetSpeedLimit_KBps( tr_session * s, tr_direction d, unsigned int KBps )
1410{
1411    tr_sessionSetSpeedLimit_Bps( s, d, toSpeedBytes( KBps ) );
1412}
1413
1414unsigned int
1415tr_sessionGetSpeedLimit_Bps( const tr_session * s, tr_direction d )
1416{
1417    assert( tr_isSession( s ) );
1418    assert( tr_isDirection( d ) );
1419
1420    return s->speedLimit_Bps[d];
1421}
1422unsigned int
1423tr_sessionGetSpeedLimit_KBps( const tr_session * s, tr_direction d )
1424{
1425    return toSpeedKBps( tr_sessionGetSpeedLimit_Bps( s, d ) );
1426}
1427
1428void
1429tr_sessionLimitSpeed( tr_session * s, tr_direction d, bool b )
1430{
1431    assert( tr_isSession( s ) );
1432    assert( tr_isDirection( d ) );
1433    assert( tr_isBool( b ) );
1434
1435    s->speedLimitEnabled[d] = b;
1436
1437    updateBandwidth( s, d );
1438}
1439
1440bool
1441tr_sessionIsSpeedLimited( const tr_session * s, tr_direction d )
1442{
1443    assert( tr_isSession( s ) );
1444    assert( tr_isDirection( d ) );
1445
1446    return s->speedLimitEnabled[d];
1447}
1448
1449/***
1450****  Alternative speed limits that are used during scheduled times
1451***/
1452
1453void
1454tr_sessionSetAltSpeed_Bps( tr_session * s, tr_direction d, unsigned int Bps )
1455{
1456    assert( tr_isSession( s ) );
1457    assert( tr_isDirection( d ) );
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, unsigned int KBps )
1466{
1467    tr_sessionSetAltSpeed_Bps( s, d, toSpeedBytes( KBps ) );
1468}
1469
1470unsigned int
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}
1478unsigned int
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    t->autoTurtleState = TR_AUTO_SWITCH_UNUSED;
1490
1491    turtleUpdateTable( t );
1492
1493    if( t->isClockEnabled )
1494    {
1495        const bool enabled = getInTurtleTime( t );
1496        useAltSpeed( s, t, enabled, true );
1497        t->autoTurtleState = autoSwitchState( enabled );
1498    }
1499}
1500
1501void
1502tr_sessionUseAltSpeedTime( tr_session * s, bool b )
1503{
1504    struct tr_turtle_info * t = &s->turtle;
1505
1506    assert( tr_isSession( s ) );
1507    assert( tr_isBool ( b ) );
1508
1509    if( t->isClockEnabled != b ) {
1510        t->isClockEnabled = b;
1511        userPokedTheClock( s, t );
1512    }
1513}
1514
1515bool
1516tr_sessionUsesAltSpeedTime( const tr_session * s )
1517{
1518    assert( tr_isSession( s ) );
1519
1520    return s->turtle.isClockEnabled;
1521}
1522
1523void
1524tr_sessionSetAltSpeedBegin( tr_session * s, int minute )
1525{
1526    assert( tr_isSession( s ) );
1527    assert( 0<=minute && minute<(60*24) );
1528
1529    if( s->turtle.beginMinute != minute ) {
1530        s->turtle.beginMinute = minute;
1531        userPokedTheClock( s, &s->turtle );
1532    }
1533}
1534
1535int
1536tr_sessionGetAltSpeedBegin( const tr_session * s )
1537{
1538    assert( tr_isSession( s ) );
1539
1540    return s->turtle.beginMinute;
1541}
1542
1543void
1544tr_sessionSetAltSpeedEnd( tr_session * s, int minute )
1545{
1546    assert( tr_isSession( s ) );
1547    assert( 0<=minute && minute<(60*24) );
1548
1549    if( s->turtle.endMinute != minute ) {
1550        s->turtle.endMinute = minute;
1551        userPokedTheClock( s, &s->turtle );
1552    }
1553}
1554
1555int
1556tr_sessionGetAltSpeedEnd( const tr_session * s )
1557{
1558    assert( tr_isSession( s ) );
1559
1560    return s->turtle.endMinute;
1561}
1562
1563void
1564tr_sessionSetAltSpeedDay( tr_session * s, tr_sched_day days )
1565{
1566    assert( tr_isSession( s ) );
1567
1568    if( s->turtle.days != days ) {
1569        s->turtle.days = days;
1570        userPokedTheClock( s, &s->turtle );
1571    }
1572}
1573
1574tr_sched_day
1575tr_sessionGetAltSpeedDay( const tr_session * s )
1576{
1577    assert( tr_isSession( s ) );
1578
1579    return s->turtle.days;
1580}
1581
1582void
1583tr_sessionUseAltSpeed( tr_session * session, bool enabled )
1584{
1585    useAltSpeed( session, &session->turtle, enabled, true );
1586}
1587
1588bool
1589tr_sessionUsesAltSpeed( const tr_session * s )
1590{
1591    assert( tr_isSession( s ) );
1592
1593    return s->turtle.isEnabled;
1594}
1595
1596void
1597tr_sessionSetAltSpeedFunc( tr_session       * session,
1598                           tr_altSpeedFunc    func,
1599                           void             * userData )
1600{
1601    assert( tr_isSession( session ) );
1602
1603    session->turtle.callback = func;
1604    session->turtle.callbackUserData = userData;
1605}
1606
1607void
1608tr_sessionClearAltSpeedFunc( tr_session * session )
1609{
1610    tr_sessionSetAltSpeedFunc( session, NULL, NULL );
1611}
1612
1613/***
1614****
1615***/
1616
1617void
1618tr_sessionSetPeerLimit( tr_session * session, uint16_t n )
1619{
1620    assert( tr_isSession( session ) );
1621
1622    session->peerLimit = n;
1623}
1624
1625uint16_t
1626tr_sessionGetPeerLimit( const tr_session * session )
1627{
1628    assert( tr_isSession( session ) );
1629
1630    return session->peerLimit;
1631}
1632
1633void
1634tr_sessionSetPeerLimitPerTorrent( tr_session  * session, uint16_t n )
1635{
1636    assert( tr_isSession( session ) );
1637
1638    session->peerLimitPerTorrent = n;
1639}
1640
1641uint16_t
1642tr_sessionGetPeerLimitPerTorrent( const tr_session * session )
1643{
1644    assert( tr_isSession( session ) );
1645
1646    return session->peerLimitPerTorrent;
1647}
1648
1649/***
1650****
1651***/
1652
1653void
1654tr_sessionSetPaused( tr_session * session, bool isPaused )
1655{
1656    assert( tr_isSession( session ) );
1657
1658    session->pauseAddedTorrent = isPaused;
1659}
1660
1661bool
1662tr_sessionGetPaused( const tr_session * session )
1663{
1664    assert( tr_isSession( session ) );
1665
1666    return session->pauseAddedTorrent;
1667}
1668
1669void
1670tr_sessionSetDeleteSource( tr_session * session, bool deleteSource )
1671{
1672    assert( tr_isSession( session ) );
1673
1674    session->deleteSourceTorrent = deleteSource;
1675}
1676
1677bool
1678tr_sessionGetDeleteSource( const tr_session * session )
1679{
1680    assert( tr_isSession( session ) );
1681
1682    return session->deleteSourceTorrent;
1683}
1684
1685/***
1686****
1687***/
1688
1689unsigned int
1690tr_sessionGetPieceSpeed_Bps( const tr_session * session, tr_direction dir )
1691{
1692    return tr_isSession( session ) ? tr_bandwidthGetPieceSpeed_Bps( &session->bandwidth, 0, dir ) : 0;
1693}
1694
1695unsigned int
1696tr_sessionGetRawSpeed_Bps( const tr_session * session, tr_direction dir )
1697{
1698    return tr_isSession( session ) ? tr_bandwidthGetRawSpeed_Bps( &session->bandwidth, 0, dir ) : 0;
1699}
1700double
1701tr_sessionGetRawSpeed_KBps( const tr_session * session, tr_direction dir )
1702{
1703    return toSpeedKBps( tr_sessionGetRawSpeed_Bps( session, dir ) );
1704}
1705
1706
1707int
1708tr_sessionCountTorrents( const tr_session * session )
1709{
1710    return tr_isSession( session ) ? session->torrentCount : 0;
1711}
1712
1713static int
1714compareTorrentByCur( const void * va, const void * vb )
1715{
1716    const tr_torrent * a = *(const tr_torrent**)va;
1717    const tr_torrent * b = *(const tr_torrent**)vb;
1718    const uint64_t     aCur = a->downloadedCur + a->uploadedCur;
1719    const uint64_t     bCur = b->downloadedCur + b->uploadedCur;
1720
1721    if( aCur != bCur )
1722        return aCur > bCur ? -1 : 1; /* close the biggest torrents first */
1723
1724    return 0;
1725}
1726
1727static void closeBlocklists( tr_session * );
1728
1729static void
1730sessionCloseImpl( void * vsession )
1731{
1732    tr_session *  session = vsession;
1733    tr_torrent *  tor;
1734    int           i, n;
1735    tr_torrent ** torrents;
1736
1737    assert( tr_isSession( session ) );
1738
1739    free_incoming_peer_port( session );
1740
1741    if( session->isLPDEnabled )
1742        tr_lpdUninit( session );
1743
1744    tr_utpClose( session );
1745    tr_dhtUninit( session );
1746
1747    event_free( session->saveTimer );
1748    session->saveTimer = NULL;
1749
1750    event_free( session->nowTimer );
1751    session->nowTimer = NULL;
1752
1753    tr_verifyClose( session );
1754    tr_sharedClose( session );
1755    tr_rpcClose( &session->rpcServer );
1756
1757    /* Close the torrents. Get the most active ones first so that
1758     * if we can't get them all closed in a reasonable amount of time,
1759     * at least we get the most important ones first. */
1760    tor = NULL;
1761    n = session->torrentCount;
1762    torrents = tr_new( tr_torrent *, session->torrentCount );
1763    for( i = 0; i < n; ++i )
1764        torrents[i] = tor = tr_torrentNext( session, tor );
1765    qsort( torrents, n, sizeof( tr_torrent* ), compareTorrentByCur );
1766    for( i = 0; i < n; ++i )
1767        tr_torrentFree( torrents[i] );
1768    tr_free( torrents );
1769
1770    /* Close the announcer *after* closing the torrents
1771       so that all the &event=stopped messages will be
1772       queued to be sent by tr_announcerClose() */
1773    tr_announcerClose( session );
1774
1775    /* and this goes *after* announcer close so that
1776       it won't be idle until the announce events are sent... */
1777    tr_webClose( session, TR_WEB_CLOSE_WHEN_IDLE );
1778
1779    tr_cacheFree( session->cache );
1780    session->cache = NULL;
1781
1782    /* gotta keep udp running long enough to send out all
1783       the &event=stopped UDP tracker messages */
1784    while( !tr_tracker_udp_is_idle( session ) ) {
1785        tr_tracker_udp_upkeep( session );
1786        tr_wait_msec( 100 );
1787    }
1788
1789    /* we had to wait until UDP trackers were closed before closing these: */
1790    evdns_base_free( session->evdns_base, 0 );
1791    session->evdns_base = NULL;
1792    tr_tracker_udp_close( session );
1793    tr_udpUninit( session );
1794
1795    tr_statsClose( session );
1796    tr_peerMgrFree( session->peerMgr );
1797
1798    closeBlocklists( session );
1799
1800    tr_fdClose( session );
1801
1802    session->isClosed = true;
1803}
1804
1805static int
1806deadlineReached( const time_t deadline )
1807{
1808    return time( NULL ) >= deadline;
1809}
1810
1811#define SHUTDOWN_MAX_SECONDS 20
1812
1813void
1814tr_sessionClose( tr_session * session )
1815{
1816    const time_t deadline = time( NULL ) + SHUTDOWN_MAX_SECONDS;
1817
1818    assert( tr_isSession( session ) );
1819
1820    dbgmsg( "shutting down transmission session %p... now is %zu, deadline is %zu", session, (size_t)time(NULL), (size_t)deadline );
1821
1822    /* close the session */
1823    tr_runInEventThread( session, sessionCloseImpl, session );
1824    while( !session->isClosed && !deadlineReached( deadline ) )
1825    {
1826        dbgmsg( "waiting for the libtransmission thread to finish" );
1827        tr_wait_msec( 100 );
1828    }
1829
1830    /* "shared" and "tracker" have live sockets,
1831     * so we need to keep the transmission thread alive
1832     * for a bit while they tell the router & tracker
1833     * that we're closing now */
1834    while( ( session->shared || session->web || session->announcer || session->announcer_udp )
1835           && !deadlineReached( deadline ) )
1836    {
1837        dbgmsg( "waiting on port unmap (%p) or announcer (%p)... now %zu deadline %zu",
1838                session->shared, session->announcer, (size_t)time(NULL), (size_t)deadline );
1839        tr_wait_msec( 100 );
1840    }
1841
1842    tr_webClose( session, TR_WEB_CLOSE_NOW );
1843
1844    /* close the libtransmission thread */
1845    tr_eventClose( session );
1846    while( session->events != NULL )
1847    {
1848        static bool forced = false;
1849        dbgmsg( "waiting for libtransmission thread to finish... now %zu deadline %zu", (size_t)time(NULL), (size_t)deadline );
1850        tr_wait_msec( 500 );
1851        if( deadlineReached( deadline ) && !forced )
1852        {
1853            dbgmsg( "calling event_loopbreak()" );
1854            forced = true;
1855            event_base_loopbreak( session->event_base );
1856        }
1857        if( deadlineReached( deadline+3 ) )
1858        {
1859            dbgmsg( "deadline+3 reached... calling break...\n" );
1860            break;
1861        }
1862    }
1863
1864    /* free the session memory */
1865    tr_bencFree( &session->removedTorrents );
1866    tr_bandwidthDestruct( &session->bandwidth );
1867    tr_bitfieldDestruct( &session->turtle.minutes );
1868    tr_lockFree( session->lock );
1869    if( session->metainfoLookup ) {
1870        tr_bencFree( session->metainfoLookup );
1871        tr_free( session->metainfoLookup );
1872    }
1873    tr_free( session->torrentDoneScript );
1874    tr_free( session->tag );
1875    tr_free( session->configDir );
1876    tr_free( session->resumeDir );
1877    tr_free( session->torrentDir );
1878    tr_free( session->downloadDir );
1879    tr_free( session->incompleteDir );
1880    tr_free( session->blocklist_url );
1881    tr_free( session->peer_congestion_algorithm );
1882    tr_free( session );
1883}
1884
1885struct sessionLoadTorrentsData
1886{
1887    tr_session * session;
1888    tr_ctor * ctor;
1889    int * setmeCount;
1890    tr_torrent ** torrents;
1891    bool done;
1892};
1893
1894static void
1895sessionLoadTorrents( void * vdata )
1896{
1897    int i;
1898    int n = 0;
1899    struct stat sb;
1900    DIR * odir = NULL;
1901    tr_list * l = NULL;
1902    tr_list * list = NULL;
1903    struct sessionLoadTorrentsData * data = vdata;
1904    const char * dirname = tr_getTorrentDir( data->session );
1905
1906    assert( tr_isSession( data->session ) );
1907
1908    tr_ctorSetSave( data->ctor, false ); /* since we already have them */
1909
1910    if( !stat( dirname, &sb )
1911      && S_ISDIR( sb.st_mode )
1912      && ( ( odir = opendir ( dirname ) ) ) )
1913    {
1914        struct dirent *d;
1915        for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
1916        {
1917            if( tr_str_has_suffix( d->d_name, ".torrent" ) )
1918            {
1919                tr_torrent * tor;
1920                char * path = tr_buildPath( dirname, d->d_name, NULL );
1921                tr_ctorSetMetainfoFromFile( data->ctor, path );
1922                if(( tor = tr_torrentNew( data->ctor, NULL )))
1923                {
1924                    tr_list_prepend( &list, tor );
1925                    ++n;
1926                }
1927                tr_free( path );
1928            }
1929        }
1930        closedir( odir );
1931    }
1932
1933    data->torrents = tr_new( tr_torrent *, n );
1934    for( i = 0, l = list; l != NULL; l = l->next )
1935        data->torrents[i++] = (tr_torrent*) l->data;
1936    assert( i == n );
1937
1938    tr_list_free( &list, NULL );
1939
1940    if( n )
1941        tr_inf( _( "Loaded %d torrents" ), n );
1942
1943    if( data->setmeCount )
1944        *data->setmeCount = n;
1945
1946    data->done = true;
1947}
1948
1949tr_torrent **
1950tr_sessionLoadTorrents( tr_session * session,
1951                        tr_ctor    * ctor,
1952                        int        * setmeCount )
1953{
1954    struct sessionLoadTorrentsData data;
1955
1956    data.session = session;
1957    data.ctor = ctor;
1958    data.setmeCount = setmeCount;
1959    data.torrents = NULL;
1960    data.done = false;
1961
1962    tr_runInEventThread( session, sessionLoadTorrents, &data );
1963    while( !data.done )
1964        tr_wait_msec( 100 );
1965
1966    return data.torrents;
1967}
1968
1969/***
1970****
1971***/
1972
1973void
1974tr_sessionSetPexEnabled( tr_session * session, bool enabled )
1975{
1976    assert( tr_isSession( session ) );
1977
1978    session->isPexEnabled = enabled != 0;
1979}
1980
1981bool
1982tr_sessionIsPexEnabled( const tr_session * session )
1983{
1984    assert( tr_isSession( session ) );
1985
1986    return session->isPexEnabled;
1987}
1988
1989bool
1990tr_sessionAllowsDHT( const tr_session * session )
1991{
1992    return tr_sessionIsDHTEnabled( session );
1993}
1994
1995bool
1996tr_sessionIsDHTEnabled( const tr_session * session )
1997{
1998    assert( tr_isSession( session ) );
1999
2000    return session->isDHTEnabled;
2001}
2002
2003static void
2004toggleDHTImpl(  void * data )
2005{
2006    tr_session * session = data;
2007    assert( tr_isSession( session ) );
2008
2009    tr_udpUninit( session );
2010    session->isDHTEnabled = !session->isDHTEnabled;
2011    tr_udpInit( session );
2012}
2013
2014void
2015tr_sessionSetDHTEnabled( tr_session * session, bool enabled )
2016{
2017    assert( tr_isSession( session ) );
2018    assert( tr_isBool( enabled ) );
2019
2020    if( ( enabled != 0 ) != ( session->isDHTEnabled != 0 ) )
2021        tr_runInEventThread( session, toggleDHTImpl, session );
2022}
2023
2024/***
2025****
2026***/
2027
2028bool
2029tr_sessionIsUTPEnabled( const tr_session * session )
2030{
2031    assert( tr_isSession( session ) );
2032
2033#ifdef WITH_UTP
2034    return session->isUTPEnabled;
2035#else
2036    return false;
2037#endif
2038}
2039
2040static void
2041toggle_utp(  void * data )
2042{
2043    tr_session * session = data;
2044    assert( tr_isSession( session ) );
2045
2046    session->isUTPEnabled = !session->isUTPEnabled;
2047
2048    tr_udpSetSocketBuffers( session );
2049
2050    /* But don't call tr_utpClose -- see reset_timer in tr-utp.c for an
2051       explanation. */
2052}
2053
2054void
2055tr_sessionSetUTPEnabled( tr_session * session, bool enabled )
2056{
2057    assert( tr_isSession( session ) );
2058    assert( tr_isBool( enabled ) );
2059
2060    if( ( enabled != 0 ) != ( session->isUTPEnabled != 0 ) )
2061        tr_runInEventThread( session, toggle_utp, session );
2062}
2063
2064/***
2065****
2066***/
2067
2068static void
2069toggleLPDImpl(  void * data )
2070{
2071    tr_session * session = data;
2072    assert( tr_isSession( session ) );
2073
2074    if( session->isLPDEnabled )
2075        tr_lpdUninit( session );
2076
2077    session->isLPDEnabled = !session->isLPDEnabled;
2078
2079    if( session->isLPDEnabled )
2080        tr_lpdInit( session, &session->public_ipv4->addr );
2081}
2082
2083void
2084tr_sessionSetLPDEnabled( tr_session * session, bool enabled )
2085{
2086    assert( tr_isSession( session ) );
2087    assert( tr_isBool( enabled ) );
2088
2089    if( ( enabled != 0 ) != ( session->isLPDEnabled != 0 ) )
2090        tr_runInEventThread( session, toggleLPDImpl, session );
2091}
2092
2093bool
2094tr_sessionIsLPDEnabled( const tr_session * session )
2095{
2096    assert( tr_isSession( session ) );
2097
2098    return session->isLPDEnabled;
2099}
2100
2101bool
2102tr_sessionAllowsLPD( const tr_session * session )
2103{
2104    return tr_sessionIsLPDEnabled( session );
2105}
2106
2107/***
2108****
2109***/
2110
2111void
2112tr_sessionSetCacheLimit_MB( tr_session * session, int max_bytes )
2113{
2114    assert( tr_isSession( session ) );
2115
2116    tr_cacheSetLimit( session->cache, toMemBytes( max_bytes ) );
2117}
2118
2119int
2120tr_sessionGetCacheLimit_MB( const tr_session * session )
2121{
2122    assert( tr_isSession( session ) );
2123
2124    return toMemMB( tr_cacheGetLimit( session->cache ) );
2125}
2126
2127/***
2128****
2129***/
2130
2131struct port_forwarding_data
2132{
2133    bool enabled;
2134    struct tr_shared * shared;
2135};
2136
2137static void
2138setPortForwardingEnabled( void * vdata )
2139{
2140    struct port_forwarding_data * data = vdata;
2141    tr_sharedTraversalEnable( data->shared, data->enabled );
2142    tr_free( data );
2143}
2144
2145void
2146tr_sessionSetPortForwardingEnabled( tr_session  * session, bool enabled )
2147{
2148    struct port_forwarding_data * d;
2149    d = tr_new0( struct port_forwarding_data, 1 );
2150    d->shared = session->shared;
2151    d->enabled = enabled;
2152    tr_runInEventThread( session, setPortForwardingEnabled, d );
2153}
2154
2155bool
2156tr_sessionIsPortForwardingEnabled( const tr_session * session )
2157{
2158    assert( tr_isSession( session ) );
2159
2160    return tr_sharedTraversalIsEnabled( session->shared );
2161}
2162
2163/***
2164****
2165***/
2166
2167static int
2168tr_stringEndsWith( const char * str, const char * end )
2169{
2170    const size_t slen = strlen( str );
2171    const size_t elen = strlen( end );
2172
2173    return slen >= elen && !memcmp( &str[slen - elen], end, elen );
2174}
2175
2176static void
2177loadBlocklists( tr_session * session )
2178{
2179    int         binCount = 0;
2180    int         newCount = 0;
2181    struct stat sb;
2182    char      * dirname;
2183    DIR *       odir = NULL;
2184    tr_list *   list = NULL;
2185    const bool  isEnabled = session->isBlocklistEnabled;
2186
2187    /* walk through the directory and find blocklists */
2188    dirname = tr_buildPath( session->configDir, "blocklists", NULL );
2189    if( !stat( dirname,
2190               &sb ) && S_ISDIR( sb.st_mode )
2191      && ( ( odir = opendir( dirname ) ) ) )
2192    {
2193        struct dirent *d;
2194        for( d = readdir( odir ); d; d = readdir( odir ) )
2195        {
2196            char * filename;
2197
2198            if( !d->d_name || d->d_name[0] == '.' ) /* skip dotfiles, ., and ..
2199                                                      */
2200                continue;
2201
2202            filename = tr_buildPath( dirname, d->d_name, NULL );
2203
2204            if( tr_stringEndsWith( filename, ".bin" ) )
2205            {
2206                /* if we don't already have this blocklist, add it */
2207                if( !tr_list_find( list, filename,
2208                                   (TrListCompareFunc)strcmp ) )
2209                {
2210                    tr_list_append( &list,
2211                                   _tr_blocklistNew( filename, isEnabled ) );
2212                    ++binCount;
2213                }
2214            }
2215            else
2216            {
2217                /* strip out the file suffix, if there is one, and add ".bin"
2218                  instead */
2219                tr_blocklist * b;
2220                const char *   dot = strrchr( d->d_name, '.' );
2221                const int      len = dot ? dot - d->d_name
2222                                         : (int)strlen( d->d_name );
2223                char         * tmp = tr_strdup_printf(
2224                                        "%s" TR_PATH_DELIMITER_STR "%*.*s.bin",
2225                                        dirname, len, len, d->d_name );
2226                b = _tr_blocklistNew( tmp, isEnabled );
2227                _tr_blocklistSetContent( b, filename );
2228                tr_list_append( &list, b );
2229                ++newCount;
2230                tr_free( tmp );
2231            }
2232
2233            tr_free( filename );
2234        }
2235
2236        closedir( odir );
2237    }
2238
2239    session->blocklists = list;
2240
2241    if( binCount )
2242        tr_dbg( "Found %d blocklists in \"%s\"", binCount, dirname );
2243    if( newCount )
2244        tr_dbg( "Found %d new blocklists in \"%s\"", newCount, dirname );
2245
2246    tr_free( dirname );
2247}
2248
2249static void
2250closeBlocklists( tr_session * session )
2251{
2252    tr_list_free( &session->blocklists,
2253                  (TrListForeachFunc)_tr_blocklistFree );
2254}
2255
2256void
2257tr_sessionReloadBlocklists( tr_session * session )
2258{
2259    closeBlocklists( session );
2260    loadBlocklists( session );
2261
2262    tr_peerMgrOnBlocklistChanged( session->peerMgr );
2263}
2264
2265int
2266tr_blocklistGetRuleCount( const tr_session * session )
2267{
2268    int       n = 0;
2269    tr_list * l;
2270
2271    assert( tr_isSession( session ) );
2272
2273    for( l = session->blocklists; l; l = l->next )
2274        n += _tr_blocklistGetRuleCount( l->data );
2275    return n;
2276}
2277
2278bool
2279tr_blocklistIsEnabled( const tr_session * session )
2280{
2281    assert( tr_isSession( session ) );
2282
2283    return session->isBlocklistEnabled;
2284}
2285
2286void
2287tr_blocklistSetEnabled( tr_session * session, bool isEnabled )
2288{
2289    tr_list * l;
2290
2291    assert( tr_isSession( session ) );
2292
2293    session->isBlocklistEnabled = isEnabled != 0;
2294
2295    for( l=session->blocklists; l!=NULL; l=l->next )
2296        _tr_blocklistSetEnabled( l->data, isEnabled );
2297}
2298
2299bool
2300tr_blocklistExists( const tr_session * session )
2301{
2302    assert( tr_isSession( session ) );
2303
2304    return session->blocklists != NULL;
2305}
2306
2307int
2308tr_blocklistSetContent( tr_session * session, const char * contentFilename )
2309{
2310    tr_list * l;
2311    int ruleCount;
2312    tr_blocklist * b;
2313    const char * defaultName = DEFAULT_BLOCKLIST_FILENAME;
2314    tr_sessionLock( session );
2315
2316    for( b = NULL, l = session->blocklists; !b && l; l = l->next )
2317        if( tr_stringEndsWith( _tr_blocklistGetFilename( l->data ),
2318                               defaultName ) )
2319            b = l->data;
2320
2321    if( !b )
2322    {
2323        char * path = tr_buildPath( session->configDir, "blocklists", defaultName, NULL );
2324        b = _tr_blocklistNew( path, session->isBlocklistEnabled );
2325        tr_list_append( &session->blocklists, b );
2326        tr_free( path );
2327    }
2328
2329    ruleCount = _tr_blocklistSetContent( b, contentFilename );
2330    tr_sessionUnlock( session );
2331    return ruleCount;
2332}
2333
2334bool
2335tr_sessionIsAddressBlocked( const tr_session * session,
2336                            const tr_address * addr )
2337{
2338    tr_list * l;
2339
2340    assert( tr_isSession( session ) );
2341
2342    for( l = session->blocklists; l; l = l->next )
2343        if( _tr_blocklistHasAddress( l->data, addr ) )
2344            return true;
2345    return false;
2346}
2347
2348void
2349tr_blocklistSetURL( tr_session * session, const char * url )
2350{
2351    if( session->blocklist_url != url )
2352    {
2353        tr_free( session->blocklist_url );
2354        session->blocklist_url = tr_strdup( url );
2355    }
2356}
2357
2358const char *
2359tr_blocklistGetURL ( const tr_session * session )
2360{
2361    return session->blocklist_url;
2362}
2363
2364
2365/***
2366****
2367***/
2368
2369static void
2370metainfoLookupInit( tr_session * session )
2371{
2372    struct stat  sb;
2373    const char * dirname = tr_getTorrentDir( session );
2374    DIR *        odir = NULL;
2375    tr_ctor *    ctor = NULL;
2376    tr_benc * lookup;
2377    int n = 0;
2378
2379    assert( tr_isSession( session ) );
2380
2381    /* walk through the directory and find the mappings */
2382    lookup = tr_new0( tr_benc, 1 );
2383    tr_bencInitDict( lookup, 0 );
2384    ctor = tr_ctorNew( session );
2385    tr_ctorSetSave( ctor, false ); /* since we already have them */
2386    if( !stat( dirname, &sb ) && S_ISDIR( sb.st_mode ) && ( ( odir = opendir( dirname ) ) ) )
2387    {
2388        struct dirent *d;
2389        while(( d = readdir( odir )))
2390        {
2391            if( tr_str_has_suffix( d->d_name, ".torrent" ) )
2392            {
2393                tr_info inf;
2394                char * path = tr_buildPath( dirname, d->d_name, NULL );
2395                tr_ctorSetMetainfoFromFile( ctor, path );
2396                if( !tr_torrentParse( ctor, &inf ) )
2397                {
2398                    ++n;
2399                    tr_bencDictAddStr( lookup, inf.hashString, path );
2400                }
2401                tr_free( path );
2402            }
2403        }
2404        closedir( odir );
2405    }
2406    tr_ctorFree( ctor );
2407
2408    session->metainfoLookup = lookup;
2409    tr_dbg( "Found %d torrents in \"%s\"", n, dirname );
2410}
2411
2412const char*
2413tr_sessionFindTorrentFile( const tr_session * session,
2414                           const char       * hashString )
2415{
2416    const char * filename = NULL;
2417    if( !session->metainfoLookup )
2418        metainfoLookupInit( (tr_session*)session );
2419    tr_bencDictFindStr( session->metainfoLookup, hashString, &filename );
2420    return filename;
2421}
2422
2423void
2424tr_sessionSetTorrentFile( tr_session * session,
2425                          const char * hashString,
2426                          const char * filename )
2427{
2428    /* since we walk session->configDir/torrents/ to build the lookup table,
2429     * and tr_sessionSetTorrentFile() is just to tell us there's a new file
2430     * in that same directory, we don't need to do anything here if the
2431     * lookup table hasn't been built yet */
2432    if( session->metainfoLookup )
2433        tr_bencDictAddStr( session->metainfoLookup, hashString, filename );
2434}
2435
2436/***
2437****
2438***/
2439
2440void
2441tr_sessionSetRPCEnabled( tr_session * session, bool isEnabled )
2442{
2443    assert( tr_isSession( session ) );
2444
2445    tr_rpcSetEnabled( session->rpcServer, isEnabled );
2446}
2447
2448bool
2449tr_sessionIsRPCEnabled( const tr_session * session )
2450{
2451    assert( tr_isSession( session ) );
2452
2453    return tr_rpcIsEnabled( session->rpcServer );
2454}
2455
2456void
2457tr_sessionSetRPCPort( tr_session * session,
2458                      tr_port      port )
2459{
2460    assert( tr_isSession( session ) );
2461
2462    tr_rpcSetPort( session->rpcServer, port );
2463}
2464
2465tr_port
2466tr_sessionGetRPCPort( const tr_session * session )
2467{
2468    assert( tr_isSession( session ) );
2469
2470    return tr_rpcGetPort( session->rpcServer );
2471}
2472
2473void
2474tr_sessionSetRPCUrl( tr_session * session,
2475                     const char * url )
2476{
2477    assert( tr_isSession( session ) );
2478
2479    tr_rpcSetUrl( session->rpcServer, url );
2480}
2481
2482const char*
2483tr_sessionGetRPCUrl( const tr_session * session )
2484{
2485    assert( tr_isSession( session ) );
2486
2487    return tr_rpcGetUrl( session->rpcServer );
2488}
2489
2490void
2491tr_sessionSetRPCCallback( tr_session * session,
2492                          tr_rpc_func  func,
2493                          void *       user_data )
2494{
2495    assert( tr_isSession( session ) );
2496
2497    session->rpc_func = func;
2498    session->rpc_func_user_data = user_data;
2499}
2500
2501void
2502tr_sessionSetRPCWhitelist( tr_session * session,
2503                           const char * whitelist )
2504{
2505    assert( tr_isSession( session ) );
2506
2507    tr_rpcSetWhitelist( session->rpcServer, whitelist );
2508}
2509
2510const char*
2511tr_sessionGetRPCWhitelist( const tr_session * session )
2512{
2513    assert( tr_isSession( session ) );
2514
2515    return tr_rpcGetWhitelist( session->rpcServer );
2516}
2517
2518void
2519tr_sessionSetRPCWhitelistEnabled( tr_session * session, bool isEnabled )
2520{
2521    assert( tr_isSession( session ) );
2522
2523    tr_rpcSetWhitelistEnabled( session->rpcServer, isEnabled );
2524}
2525
2526bool
2527tr_sessionGetRPCWhitelistEnabled( const tr_session * session )
2528{
2529    assert( tr_isSession( session ) );
2530
2531    return tr_rpcGetWhitelistEnabled( session->rpcServer );
2532}
2533
2534
2535void
2536tr_sessionSetRPCPassword( tr_session * session,
2537                          const char * password )
2538{
2539    assert( tr_isSession( session ) );
2540
2541    tr_rpcSetPassword( session->rpcServer, password );
2542}
2543
2544const char*
2545tr_sessionGetRPCPassword( const tr_session * session )
2546{
2547    assert( tr_isSession( session ) );
2548
2549    return tr_rpcGetPassword( session->rpcServer );
2550}
2551
2552void
2553tr_sessionSetRPCUsername( tr_session * session,
2554                          const char * username )
2555{
2556    assert( tr_isSession( session ) );
2557
2558    tr_rpcSetUsername( session->rpcServer, username );
2559}
2560
2561const char*
2562tr_sessionGetRPCUsername( const tr_session * session )
2563{
2564    assert( tr_isSession( session ) );
2565
2566    return tr_rpcGetUsername( session->rpcServer );
2567}
2568
2569void
2570tr_sessionSetRPCPasswordEnabled( tr_session * session, bool isEnabled )
2571{
2572    assert( tr_isSession( session ) );
2573
2574    tr_rpcSetPasswordEnabled( session->rpcServer, isEnabled );
2575}
2576
2577bool
2578tr_sessionIsRPCPasswordEnabled( const tr_session * session )
2579{
2580    assert( tr_isSession( session ) );
2581
2582    return tr_rpcIsPasswordEnabled( session->rpcServer );
2583}
2584
2585const char *
2586tr_sessionGetRPCBindAddress( const tr_session * session )
2587{
2588    assert( tr_isSession( session ) );
2589
2590    return tr_rpcGetBindAddress( session->rpcServer );
2591}
2592
2593/****
2594*****
2595****/
2596
2597bool
2598tr_sessionIsTorrentDoneScriptEnabled( const tr_session * session )
2599{
2600    assert( tr_isSession( session ) );
2601
2602    return session->isTorrentDoneScriptEnabled;
2603}
2604
2605void
2606tr_sessionSetTorrentDoneScriptEnabled( tr_session * session, bool isEnabled )
2607{
2608    assert( tr_isSession( session ) );
2609    assert( tr_isBool( isEnabled ) );
2610
2611    session->isTorrentDoneScriptEnabled = isEnabled;
2612}
2613
2614const char *
2615tr_sessionGetTorrentDoneScript( const tr_session * session )
2616{
2617    assert( tr_isSession( session ) );
2618
2619    return session->torrentDoneScript;
2620}
2621
2622void
2623tr_sessionSetTorrentDoneScript( tr_session * session, const char * scriptFilename )
2624{
2625    assert( tr_isSession( session ) );
2626
2627    if( session->torrentDoneScript != scriptFilename )
2628    {
2629        tr_free( session->torrentDoneScript );
2630        session->torrentDoneScript = tr_strdup( scriptFilename );
2631    }
2632}
2633
2634/***
2635****
2636***/
2637
2638void
2639tr_sessionSetQueueSize( tr_session * session, tr_direction dir, int n )
2640{
2641    assert( tr_isSession( session ) );
2642    assert( tr_isDirection( dir ) );
2643
2644    session->queueSize[dir] = n;
2645}
2646
2647int
2648tr_sessionGetQueueSize( const tr_session * session, tr_direction dir )
2649{
2650    assert( tr_isSession( session ) );
2651    assert( tr_isDirection( dir ) );
2652
2653    return session->queueSize[dir];
2654}
2655
2656void
2657tr_sessionSetQueueEnabled( tr_session * session, tr_direction dir, bool is_enabled )
2658{
2659    assert( tr_isSession( session ) );
2660    assert( tr_isDirection( dir ) );
2661    assert( tr_isBool( is_enabled ) );
2662
2663    session->queueEnabled[dir] = is_enabled;
2664}
2665
2666bool
2667tr_sessionGetQueueEnabled( const tr_session * session, tr_direction dir )
2668{
2669    assert( tr_isSession( session ) );
2670    assert( tr_isDirection( dir ) );
2671
2672    return session->queueEnabled[dir];
2673}
2674
2675void
2676tr_sessionSetQueueStalledMinutes( tr_session * session, int minutes )
2677{
2678    assert( tr_isSession( session ) );
2679    assert( minutes > 0 );
2680
2681    session->queueStalledMinutes = minutes;
2682}
2683
2684void
2685tr_sessionSetQueueStalledEnabled( tr_session * session, bool is_enabled )
2686{
2687    assert( tr_isSession( session ) );
2688    assert( tr_isBool( is_enabled ) );
2689
2690    session->stalledEnabled = is_enabled;
2691}
2692
2693bool
2694tr_sessionGetQueueStalledEnabled( const tr_session * session )
2695{
2696    assert( tr_isSession( session ) );
2697
2698    return session->stalledEnabled;
2699}
2700
2701int
2702tr_sessionGetQueueStalledMinutes( const tr_session * session )
2703{
2704    assert( tr_isSession( session ) );
2705
2706    return session->queueStalledMinutes;
2707}
2708
2709tr_torrent *
2710tr_sessionGetNextQueuedTorrent( tr_session * session, tr_direction direction )
2711{
2712    tr_torrent * tor = NULL;
2713    tr_torrent * best_tor = NULL;
2714    int best_position = INT_MAX;
2715
2716    assert( tr_isSession( session ) );
2717    assert( tr_isDirection( direction ) );
2718
2719    while(( tor = tr_torrentNext( session, tor )))
2720    {
2721        int position;
2722
2723        if( !tr_torrentIsQueued( tor ) )
2724            continue;
2725        if( direction != tr_torrentGetQueueDirection( tor ) )
2726            continue;
2727
2728        position = tr_torrentGetQueuePosition( tor );
2729        if( best_position > position ) {
2730            best_position = position;
2731            best_tor = tor;
2732        }
2733    }
2734
2735    return best_tor;
2736}
2737
2738int
2739tr_sessionCountQueueFreeSlots( tr_session * session, tr_direction dir )
2740{
2741    tr_torrent * tor;
2742    int active_count;
2743    const int max = tr_sessionGetQueueSize( session, dir );
2744    const tr_torrent_activity activity = dir == TR_UP ? TR_STATUS_SEED : TR_STATUS_DOWNLOAD;
2745
2746    if( !tr_sessionGetQueueEnabled( session, dir ) )
2747        return INT_MAX;
2748
2749    tor = NULL;
2750    active_count = 0;
2751    while(( tor = tr_torrentNext( session, tor )))
2752        if( !tr_torrentIsStalled( tor ) )
2753            if( tr_torrentGetActivity( tor ) == activity )
2754                ++active_count;
2755
2756    if( active_count >= max )
2757        return 0;
2758
2759    return max - active_count;
2760}
Note: See TracBrowser for help on using the repository browser.