source: trunk/libtransmission/session.c @ 10798

Last change on this file since 10798 was 10798, checked in by charles, 12 years ago

(trunk) #1521 "memory cache to reduce disk IO" -- commit block-cache-rc1.diff to trunk for the nightlies.

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