source: trunk/libtransmission/session.c @ 10135

Last change on this file since 10135 was 10135, checked in by charles, 13 years ago

(trunk daemon) #2877 "Don't let the daemon launch if settings.json is corrupt" -- implemented in trunk for 1.90

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