source: trunk/libtransmission/session.c @ 10352

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

(trunk libT) #3026 "Recent T doesn't honor weekend speed limit setting" -- fixed in trunk for 2.00

  • Property svn:keywords set to Date Rev Author Id
File size: 64.2 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 10352 2010-03-10 22:19:31Z 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    session->buffer = tr_valloc( SESSION_BUFFER_SIZE );
503    tr_bencInitList( &session->removedTorrents, 0 );
504
505    /* nice to start logging at the very beginning */
506    if( tr_bencDictFindInt( clientSettings, TR_PREFS_KEY_MSGLEVEL, &i ) )
507        tr_setMessageLevel( i );
508
509    /* start the libtransmission thread */
510    tr_netInit( ); /* must go before tr_eventInit */
511    tr_eventInit( session );
512    assert( session->events != NULL );
513
514    /* run the rest in the libtransmission thread */
515    data.done = FALSE;
516    data.session = session;
517    data.configDir = configDir;
518    data.messageQueuingEnabled = messageQueuingEnabled;
519    data.clientSettings = clientSettings;
520    tr_runInEventThread( session, tr_sessionInitImpl, &data );
521    while( !data.done )
522        tr_wait_msec( 100 );
523
524    return session;
525}
526
527static void turtleCheckClock( tr_session * session, struct tr_turtle_info * t, tr_bool byUser );
528
529static void
530onNowTimer( int foo UNUSED, short bar UNUSED, void * vsession )
531{
532    int usec;
533    const int min = 100;
534    const int max = 999999;
535    struct timeval tv;
536    tr_session * session = vsession;
537
538    assert( tr_isSession( session ) );
539    assert( session->nowTimer != NULL );
540
541    /* schedule the next timer for right after the next second begins */
542    gettimeofday( &tv, NULL );
543    usec = 1000000 - tv.tv_usec;
544    if( usec > max ) usec = max;
545    if( usec < min ) usec = min;
546    tr_timerAdd( session->nowTimer, 0, usec );
547    /* fprintf( stderr, "time %zu sec, %zu microsec\n", (size_t)tr_time(), (size_t)tv.tv_usec ); */
548
549    /* tr_session things to do once per second */
550    tr_timeUpdate( tv.tv_sec );
551    turtleCheckClock( session, &session->turtle, FALSE );
552}
553
554static void loadBlocklists( tr_session * session );
555
556static void
557tr_sessionInitImpl( void * vdata )
558{
559    tr_benc settings;
560    struct init_data * data = vdata;
561    tr_benc * clientSettings = data->clientSettings;
562    tr_session * session = data->session;
563
564    assert( tr_amInEventThread( session ) );
565    assert( tr_bencIsDict( clientSettings ) );
566
567    dbgmsg( "tr_sessionInit: the session's top-level bandwidth object is %p",
568            session->bandwidth );
569
570    tr_bencInitDict( &settings, 0 );
571    tr_sessionGetDefaultSettings( data->configDir, &settings );
572    tr_bencMergeDicts( &settings, clientSettings );
573
574    session->nowTimer = tr_new0( struct event, 1 );
575    evtimer_set( session->nowTimer, onNowTimer, session );
576    onNowTimer( 0, 0, session );
577
578#ifndef WIN32
579    /* Don't exit when writing on a broken socket */
580    signal( SIGPIPE, SIG_IGN );
581#endif
582
583    tr_setMessageQueuing( data->messageQueuingEnabled );
584
585    tr_setConfigDir( session, data->configDir );
586
587    session->peerMgr = tr_peerMgrNew( session );
588
589    session->shared = tr_sharedInit( session );
590
591    /**
592    ***  Blocklist
593    **/
594
595    {
596        char * filename = tr_buildPath( session->configDir, "blocklists", NULL );
597        tr_mkdirp( filename, 0777 );
598        tr_free( filename );
599        loadBlocklists( session );
600    }
601
602    assert( tr_isSession( session ) );
603
604    session->saveTimer = tr_new0( struct event, 1 );
605    evtimer_set( session->saveTimer, onSaveTimer, session );
606    tr_timerAdd( session->saveTimer, SAVE_INTERVAL_SECS, 0 );
607
608    tr_announcerInit( session );
609
610    /* first %s is the application name
611       second %s is the version number */
612    tr_inf( _( "%s %s started" ), TR_NAME, LONG_VERSION_STRING );
613
614    tr_statsInit( session );
615
616    tr_webInit( session );
617
618    tr_sessionSet( session, &settings );
619
620    if( session->isDHTEnabled )
621    {
622        tr_dhtInit( session, &session->public_ipv4->addr );
623    }
624
625    /* cleanup */
626    tr_bencFree( &settings );
627    data->done = TRUE;
628}
629
630static void turtleBootstrap( tr_session *, struct tr_turtle_info * );
631
632static void
633sessionSetImpl( void * vdata )
634{
635    int64_t i;
636    double  d;
637    tr_bool boolVal;
638    const char * str;
639    struct tr_bindinfo b;
640    struct init_data * data = vdata;
641    tr_session * session = data->session;
642    tr_benc * settings = data->clientSettings;
643    struct tr_turtle_info * turtle = &session->turtle;
644
645    assert( tr_isSession( session ) );
646    assert( tr_bencIsDict( settings ) );
647    assert( tr_amInEventThread( session ) );
648
649    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_MSGLEVEL, &i ) )
650        tr_setMessageLevel( i );
651
652    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_UMASK, &i ) ) {
653        session->umask = (mode_t)i;
654        umask( session->umask );
655    }
656
657    /* misc features */
658    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_LAZY_BITFIELD, &boolVal ) )
659        tr_sessionSetLazyBitfieldEnabled( session, boolVal );
660    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i ) )
661        tr_sessionSetPeerLimitPerTorrent( session, i );
662    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) )
663        tr_sessionSetPexEnabled( session, boolVal );
664    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_DHT_ENABLED, &boolVal ) )
665        tr_sessionSetDHTEnabled( session, boolVal );
666    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ENCRYPTION, &i ) )
667        tr_sessionSetEncryption( session, i );
668    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_PEER_SOCKET_TOS, &i ) )
669        session->peerSocketTOS = i;
670    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, &boolVal ) )
671        tr_blocklistSetEnabled( session, boolVal );
672
673    /* files and directories */
674    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_PREALLOCATION, &i ) )
675        session->preallocationMode = i;
676    if( tr_bencDictFindStr( settings, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
677        tr_sessionSetDownloadDir( session, str );
678    if( tr_bencDictFindStr( settings, TR_PREFS_KEY_INCOMPLETE_DIR, &str ) )
679        tr_sessionSetIncompleteDir( session, str );
680    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, &boolVal ) )
681        tr_sessionSetIncompleteDirEnabled( session, boolVal );
682    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_RENAME_PARTIAL_FILES, &boolVal ) )
683        tr_sessionSetIncompleteFileNamingEnabled( session, boolVal );
684
685    /* proxies */
686    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_PROXY_ENABLED, &boolVal ) )
687        tr_sessionSetProxyEnabled( session, boolVal );
688    if( tr_bencDictFindStr( settings, TR_PREFS_KEY_PROXY, &str ) )
689        tr_sessionSetProxy( session, str );
690    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_PROXY_PORT, &i ) )
691        tr_sessionSetProxyPort( session, i );
692    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_PROXY_TYPE, &i ) )
693        tr_sessionSetProxyType( session, i );
694    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_PROXY_AUTH_ENABLED, &boolVal ) )
695        tr_sessionSetProxyAuthEnabled( session, boolVal );
696    if( tr_bencDictFindStr( settings, TR_PREFS_KEY_PROXY_USERNAME, &str ) )
697        tr_sessionSetProxyUsername( session, str );
698    if( tr_bencDictFindStr( settings, TR_PREFS_KEY_PROXY_PASSWORD, &str ) )
699        tr_sessionSetProxyPassword( session, str );
700
701    /* rpc server */
702    if( session->rpcServer != NULL ) /* close the old one */
703        tr_rpcClose( &session->rpcServer );
704    session->rpcServer = tr_rpcInit( session, settings );
705
706    /* public addresses */
707
708    free_incoming_peer_port( session );
709
710    str = TR_PREFS_KEY_BIND_ADDRESS_IPV4;
711    tr_bencDictFindStr( settings, TR_PREFS_KEY_BIND_ADDRESS_IPV4, &str );
712    if( !tr_pton( str, &b.addr ) || ( b.addr.type != TR_AF_INET ) )
713        b.addr = tr_inaddr_any;
714    b.socket = -1;
715    session->public_ipv4 = tr_memdup( &b, sizeof( struct tr_bindinfo ) );
716    tr_webSetInterface( session, &session->public_ipv4->addr );
717
718    str = TR_PREFS_KEY_BIND_ADDRESS_IPV6;
719    tr_bencDictFindStr( settings, TR_PREFS_KEY_BIND_ADDRESS_IPV6, &str );
720    if( !tr_pton( str, &b.addr ) || ( b.addr.type != TR_AF_INET6 ) )
721        b.addr = tr_in6addr_any;
722    b.socket = -1;
723    session->public_ipv6 = tr_memdup( &b, sizeof( struct tr_bindinfo ) );
724
725    /* incoming peer port */
726    if( tr_bencDictFindInt ( settings, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW, &i ) )
727        session->randomPortLow = i;
728    if( tr_bencDictFindInt ( settings, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH, &i ) )
729        session->randomPortHigh = i;
730    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, &boolVal ) )
731        tr_sessionSetPeerPortRandomOnStart( session, boolVal );
732    if( !tr_bencDictFindInt( settings, TR_PREFS_KEY_PEER_PORT, &i ) )
733        i = session->peerPort;
734    tr_sessionSetPeerPort( session, boolVal ? getRandomPort( session ) : i );
735    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) )
736        tr_sessionSetPortForwardingEnabled( session, boolVal );
737
738    /* file and peer socket limits */
739    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &i ) )
740        tr_fdSetPeerLimit( session, i );
741    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_OPEN_FILE_LIMIT, &i ) )
742        tr_fdSetFileLimit( session, i );
743
744    /**
745    **/
746
747    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, &i ) )
748        session->uploadSlotsPerTorrent = i;
749
750    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_USPEED, &i ) )
751        tr_sessionSetSpeedLimit( session, TR_UP, i );
752    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_USPEED_ENABLED, &boolVal ) )
753        tr_sessionLimitSpeed( session, TR_UP, boolVal );
754
755    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_DSPEED, &i ) )
756        tr_sessionSetSpeedLimit( session, TR_DOWN, i );
757    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_DSPEED_ENABLED, &boolVal ) )
758        tr_sessionLimitSpeed( session, TR_DOWN, boolVal );
759
760    if( tr_bencDictFindReal( settings, TR_PREFS_KEY_RATIO, &d ) )
761        tr_sessionSetRatioLimit( session, d );
762    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_RATIO_ENABLED, &boolVal ) )
763        tr_sessionSetRatioLimited( session, boolVal );
764
765    /**
766    ***  Turtle Mode
767    **/
768
769    /* update the turtle mode's fields */
770    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ALT_SPEED_UP, &i ) )
771        turtle->speedLimit[TR_UP] = i;
772    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ALT_SPEED_DOWN, &i ) )
773        turtle->speedLimit[TR_DOWN] = i;
774    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &i ) )
775        turtle->beginMinute = i;
776    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ALT_SPEED_TIME_END, &i ) )
777        turtle->endMinute = i;
778    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &i ) )
779        turtle->days = i;
780    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &boolVal ) )
781        turtle->isClockEnabled = boolVal;
782    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_ALT_SPEED_ENABLED, &boolVal ) )
783        turtle->isEnabled = boolVal;
784    turtleBootstrap( session, turtle );
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_sessionGetBuffer( tr_session * session )
897{
898    assert( tr_isSession( session ) );
899    assert( !session->bufferInUse );
900    assert( tr_amInEventThread( session ) );
901
902    session->bufferInUse = TRUE;
903    return session->buffer;
904}
905
906void
907tr_sessionReleaseBuffer( tr_session * session )
908{
909    assert( tr_isSession( session ) );
910    assert( session->bufferInUse );
911    assert( tr_amInEventThread( session ) );
912
913    session->bufferInUse = FALSE;
914}
915
916void
917tr_sessionLock( tr_session * session )
918{
919    assert( tr_isSession( session ) );
920
921    tr_lockLock( session->lock );
922}
923
924void
925tr_sessionUnlock( tr_session * session )
926{
927    assert( tr_isSession( session ) );
928
929    tr_lockUnlock( session->lock );
930}
931
932tr_bool
933tr_sessionIsLocked( const tr_session * session )
934{
935    return tr_isSession( session ) && tr_lockHave( session->lock );
936}
937
938/***********************************************************************
939 * tr_setBindPort
940 ***********************************************************************
941 *
942 **********************************************************************/
943
944static void
945setPeerPort( void * session )
946{
947    tr_torrent * tor = NULL;
948
949    assert( tr_isSession( session ) );
950
951    close_incoming_peer_port( session );
952    open_incoming_peer_port( session );
953    tr_sharedPortChanged( session );
954
955    while(( tor = tr_torrentNext( session, tor )))
956        tr_torrentChangeMyPort( tor );
957}
958
959void
960tr_sessionSetPeerPort( tr_session * session, tr_port port )
961{
962    assert( tr_isSession( session ) );
963
964    if( session->peerPort != port )
965    {
966        session->peerPort = port;
967
968        tr_runInEventThread( session, setPeerPort, session );
969    }
970}
971
972tr_port
973tr_sessionGetPeerPort( const tr_session * session )
974{
975    assert( tr_isSession( session ) );
976
977    return session->peerPort;
978}
979
980tr_port
981tr_sessionSetPeerPortRandom( tr_session * session )
982{
983    assert( tr_isSession( session ) );
984
985    tr_sessionSetPeerPort( session, getRandomPort( session ) );
986    return session->peerPort;
987}
988
989void
990tr_sessionSetPeerPortRandomOnStart( tr_session * session,
991                                    tr_bool random )
992{
993    assert( tr_isSession( session ) );
994
995    session->isPortRandom = random;
996}
997
998tr_bool
999tr_sessionGetPeerPortRandomOnStart( tr_session * session )
1000{
1001    assert( tr_isSession( session ) );
1002
1003    return session->isPortRandom;
1004}
1005
1006tr_port_forwarding
1007tr_sessionGetPortForwarding( const tr_session * session )
1008{
1009    assert( tr_isSession( session ) );
1010
1011    return tr_sharedTraversalStatus( session->shared );
1012}
1013
1014/***
1015****
1016***/
1017
1018static void
1019updateSeedRatio( tr_session * session )
1020{
1021    tr_torrent * tor = NULL;
1022
1023    while(( tor = tr_torrentNext( session, tor )))
1024        tor->needsSeedRatioCheck = TRUE;
1025}
1026
1027void
1028tr_sessionSetRatioLimited( tr_session * session, tr_bool isLimited )
1029{
1030    assert( tr_isSession( session ) );
1031
1032    session->isRatioLimited = isLimited;
1033    updateSeedRatio( session );
1034}
1035
1036void
1037tr_sessionSetRatioLimit( tr_session * session, double desiredRatio )
1038{
1039    assert( tr_isSession( session ) );
1040
1041    session->desiredRatio = desiredRatio;
1042    updateSeedRatio( session );
1043}
1044
1045tr_bool
1046tr_sessionIsRatioLimited( const tr_session  * session )
1047{
1048    assert( tr_isSession( session ) );
1049
1050    return session->isRatioLimited;
1051}
1052
1053double
1054tr_sessionGetRatioLimit( const tr_session * session )
1055{
1056    assert( tr_isSession( session ) );
1057
1058    return session->desiredRatio;
1059}
1060
1061/***
1062****
1063****  SPEED LIMITS
1064****
1065***/
1066
1067tr_bool
1068tr_sessionGetActiveSpeedLimit( const tr_session * session, tr_direction dir, int * setme )
1069{
1070    int isLimited = TRUE;
1071
1072    if( !tr_isSession( session ) )
1073        return FALSE;
1074
1075    if( tr_sessionUsesAltSpeed( session ) )
1076        *setme = tr_sessionGetAltSpeed( session, dir );
1077    else if( tr_sessionIsSpeedLimited( session, dir ) )
1078        *setme = tr_sessionGetSpeedLimit( session, dir );
1079    else
1080        isLimited = FALSE;
1081
1082    return isLimited;
1083}
1084
1085static void
1086updateBandwidth( tr_session * session, tr_direction dir )
1087{
1088    int limit = 0;
1089    const tr_bool isLimited = tr_sessionGetActiveSpeedLimit( session, dir, &limit );
1090    const tr_bool zeroCase = isLimited && !limit;
1091
1092    tr_bandwidthSetLimited( session->bandwidth, dir, isLimited && !zeroCase );
1093
1094    tr_bandwidthSetDesiredSpeed( session->bandwidth, dir, limit );
1095}
1096
1097enum
1098{
1099    MINUTES_PER_HOUR = 60,
1100    MINUTES_PER_DAY = MINUTES_PER_HOUR * 24,
1101    MINUTES_PER_WEEK = MINUTES_PER_DAY * 7
1102};
1103
1104static void
1105turtleUpdateTable( struct tr_turtle_info * t )
1106{
1107    int day;
1108    tr_bitfield * b = &t->minutes;
1109
1110    tr_bitfieldClear( b );
1111
1112    for( day=0; day<7; ++day )
1113    {
1114        if( t->days & (1<<day) )
1115        {
1116            int i;
1117            const time_t begin = t->beginMinute;
1118            time_t end = t->endMinute;
1119
1120            if( end <= begin )
1121                end += MINUTES_PER_DAY;
1122
1123            for( i=begin; i<end; ++i )
1124                tr_bitfieldAdd( b, (i+day*MINUTES_PER_DAY) % MINUTES_PER_WEEK );
1125        }
1126    }
1127}
1128
1129static void
1130altSpeedToggled( void * vsession )
1131{
1132    tr_session * session = vsession;
1133    struct tr_turtle_info * t = &session->turtle;
1134
1135    assert( tr_isSession( session ) );
1136
1137    updateBandwidth( session, TR_UP );
1138    updateBandwidth( session, TR_DOWN );
1139
1140    if( t->callback != NULL )
1141        (*t->callback)( session, t->isEnabled, t->changedByUser, t->callbackUserData );
1142}
1143
1144static void
1145useAltSpeed( tr_session * s, struct tr_turtle_info * t, tr_bool enabled, tr_bool byUser )
1146{
1147    assert( tr_isSession( s ) );
1148    assert( t != NULL );
1149    assert( tr_isBool( enabled ) );
1150    assert( tr_isBool( byUser ) );
1151
1152    if( t->isEnabled != enabled )
1153    {
1154        t->isEnabled = enabled;
1155        t->changedByUser = byUser;
1156        tr_runInEventThread( s, altSpeedToggled, s );
1157    }
1158}
1159
1160static tr_bool
1161testTurtleTime( const struct tr_turtle_info * t )
1162{
1163    struct tm tm;
1164    size_t minute_of_the_week;
1165    const time_t now = tr_time( );
1166
1167    tr_localtime_r( &now, &tm );
1168
1169    minute_of_the_week = tm.tm_wday * MINUTES_PER_DAY
1170                       + tm.tm_hour * MINUTES_PER_HOUR
1171                       + tm.tm_min;
1172
1173    if( minute_of_the_week >= MINUTES_PER_WEEK ) /* leap minutes? */
1174        minute_of_the_week = MINUTES_PER_WEEK - 1;
1175
1176    return tr_bitfieldHasFast( &t->minutes, minute_of_the_week );
1177}
1178
1179static void
1180turtleCheckClock( tr_session * session, struct tr_turtle_info * t, tr_bool byUser )
1181{
1182    if( t->isClockEnabled )
1183    {
1184        const tr_bool hit = testTurtleTime( t );
1185
1186        if( hit != t->isEnabled )
1187        {
1188            tr_inf( "Time to turn %s turtle mode!", (hit?"on":"off") );
1189            useAltSpeed( session, t, hit, byUser );
1190        }
1191    }
1192}
1193
1194/* Called after the turtle's fields are loaded from an outside source.
1195 * It initializes the implementation fields
1196 * and turns on turtle mode if the clock settings say to. */
1197static void
1198turtleBootstrap( tr_session * session, struct tr_turtle_info * turtle )
1199{
1200    turtle->changedByUser = FALSE;
1201
1202    tr_bitfieldConstruct( &turtle->minutes, MINUTES_PER_WEEK );
1203
1204    turtleUpdateTable( turtle );
1205
1206    if( turtle->isClockEnabled )
1207        turtle->isEnabled = testTurtleTime( turtle );
1208
1209    altSpeedToggled( session );
1210}
1211
1212/***
1213****  Primary session speed limits
1214***/
1215
1216void
1217tr_sessionSetSpeedLimit( tr_session * s, tr_direction d, int KB_s )
1218{
1219    assert( tr_isSession( s ) );
1220    assert( tr_isDirection( d ) );
1221    assert( KB_s >= 0 );
1222
1223    s->speedLimit[d] = KB_s;
1224
1225    updateBandwidth( s, d );
1226}
1227
1228int
1229tr_sessionGetSpeedLimit( const tr_session * s, tr_direction d )
1230{
1231    assert( tr_isSession( s ) );
1232    assert( tr_isDirection( d ) );
1233
1234    return s->speedLimit[d];
1235}
1236
1237void
1238tr_sessionLimitSpeed( tr_session * s, tr_direction d, tr_bool b )
1239{
1240    assert( tr_isSession( s ) );
1241    assert( tr_isDirection( d ) );
1242    assert( tr_isBool( b ) );
1243
1244    s->speedLimitEnabled[d] = b;
1245
1246    updateBandwidth( s, d );
1247}
1248
1249tr_bool
1250tr_sessionIsSpeedLimited( const tr_session * s, tr_direction d )
1251{
1252    assert( tr_isSession( s ) );
1253    assert( tr_isDirection( d ) );
1254
1255    return s->speedLimitEnabled[d];
1256}
1257
1258/***
1259****  Alternative speed limits that are used during scheduled times
1260***/
1261
1262void
1263tr_sessionSetAltSpeed( tr_session * s, tr_direction d, int KB_s )
1264{
1265    assert( tr_isSession( s ) );
1266    assert( tr_isDirection( d ) );
1267    assert( KB_s >= 0 );
1268
1269    s->turtle.speedLimit[d] = KB_s;
1270
1271    updateBandwidth( s, d );
1272}
1273
1274int
1275tr_sessionGetAltSpeed( const tr_session * s, tr_direction d )
1276{
1277    assert( tr_isSession( s ) );
1278    assert( tr_isDirection( d ) );
1279
1280    return s->turtle.speedLimit[d];
1281}
1282
1283static void
1284userPokedTheClock( tr_session * s, struct tr_turtle_info * t )
1285{
1286    tr_dbg( "Refreshing the turtle mode clock due to user changes" );
1287
1288    turtleUpdateTable( t );
1289
1290    if( t->isClockEnabled )
1291        useAltSpeed( s, t, testTurtleTime( t ), TRUE );
1292}
1293
1294void
1295tr_sessionUseAltSpeedTime( tr_session * s, tr_bool b )
1296{
1297    struct tr_turtle_info * t = &s->turtle;
1298
1299    assert( tr_isSession( s ) );
1300    assert( tr_isBool ( b ) );
1301
1302    if( t->isClockEnabled != b ) {
1303        t->isClockEnabled = b;
1304        userPokedTheClock( s, t );
1305    }
1306}
1307
1308tr_bool
1309tr_sessionUsesAltSpeedTime( const tr_session * s )
1310{
1311    assert( tr_isSession( s ) );
1312
1313    return s->turtle.isClockEnabled;
1314}
1315
1316void
1317tr_sessionSetAltSpeedBegin( tr_session * s, int minute )
1318{
1319    assert( tr_isSession( s ) );
1320    assert( 0<=minute && minute<(60*24) );
1321
1322    if( s->turtle.beginMinute != minute ) {
1323        s->turtle.beginMinute = minute;
1324        userPokedTheClock( s, &s->turtle );
1325    }
1326}
1327
1328int
1329tr_sessionGetAltSpeedBegin( const tr_session * s )
1330{
1331    assert( tr_isSession( s ) );
1332
1333    return s->turtle.beginMinute;
1334}
1335
1336void
1337tr_sessionSetAltSpeedEnd( tr_session * s, int minute )
1338{
1339    assert( tr_isSession( s ) );
1340    assert( 0<=minute && minute<(60*24) );
1341
1342    if( s->turtle.endMinute != minute ) {
1343        s->turtle.endMinute = minute;
1344        userPokedTheClock( s, &s->turtle );
1345    }
1346}
1347
1348int
1349tr_sessionGetAltSpeedEnd( const tr_session * s )
1350{
1351    assert( tr_isSession( s ) );
1352
1353    return s->turtle.endMinute;
1354}
1355
1356void
1357tr_sessionSetAltSpeedDay( tr_session * s, tr_sched_day days )
1358{
1359    assert( tr_isSession( s ) );
1360
1361    if( s->turtle.days != days ) {
1362        s->turtle.days = days;
1363        userPokedTheClock( s, &s->turtle );
1364    }
1365}
1366
1367tr_sched_day
1368tr_sessionGetAltSpeedDay( const tr_session * s )
1369{
1370    assert( tr_isSession( s ) );
1371
1372    return s->turtle.days;
1373}
1374
1375void
1376tr_sessionUseAltSpeed( tr_session * session, tr_bool enabled )
1377{
1378    useAltSpeed( session, &session->turtle, enabled, TRUE );
1379}
1380
1381tr_bool
1382tr_sessionUsesAltSpeed( const tr_session * s )
1383{
1384    assert( tr_isSession( s ) );
1385
1386    return s->turtle.isEnabled;
1387}
1388
1389void
1390tr_sessionSetAltSpeedFunc( tr_session       * session,
1391                           tr_altSpeedFunc    func,
1392                           void             * userData )
1393{
1394    assert( tr_isSession( session ) );
1395
1396    session->turtle.callback = func;
1397    session->turtle.callbackUserData = userData;
1398}
1399
1400void
1401tr_sessionClearAltSpeedFunc( tr_session * session )
1402{
1403    tr_sessionSetAltSpeedFunc( session, NULL, NULL );
1404}
1405
1406/***
1407****
1408***/
1409
1410void
1411tr_sessionSetPeerLimit( tr_session * session, uint16_t maxGlobalPeers )
1412{
1413    assert( tr_isSession( session ) );
1414
1415    tr_fdSetPeerLimit( session, maxGlobalPeers );
1416}
1417
1418uint16_t
1419tr_sessionGetPeerLimit( const tr_session * session )
1420{
1421    assert( tr_isSession( session ) );
1422
1423    return tr_fdGetPeerLimit( session );
1424}
1425
1426void
1427tr_sessionSetPeerLimitPerTorrent( tr_session  * session, uint16_t n )
1428{
1429    assert( tr_isSession( session ) );
1430
1431    session->peerLimitPerTorrent = n;
1432}
1433
1434uint16_t
1435tr_sessionGetPeerLimitPerTorrent( const tr_session * session )
1436{
1437    assert( tr_isSession( session ) );
1438
1439    return session->peerLimitPerTorrent;
1440}
1441
1442/***
1443****
1444***/
1445
1446double
1447tr_sessionGetPieceSpeed( const tr_session * session, tr_direction dir )
1448{
1449    return tr_isSession( session ) ? tr_bandwidthGetPieceSpeed( session->bandwidth, 0, dir ) : 0.0;
1450}
1451
1452double
1453tr_sessionGetRawSpeed( const tr_session * session, tr_direction dir )
1454{
1455    return tr_isSession( session ) ? tr_bandwidthGetRawSpeed( session->bandwidth, 0, dir ) : 0.0;
1456}
1457
1458int
1459tr_sessionCountTorrents( const tr_session * session )
1460{
1461    return tr_isSession( session ) ? session->torrentCount : 0;
1462}
1463
1464static int
1465compareTorrentByCur( const void * va, const void * vb )
1466{
1467    const tr_torrent * a = *(const tr_torrent**)va;
1468    const tr_torrent * b = *(const tr_torrent**)vb;
1469    const uint64_t     aCur = a->downloadedCur + a->uploadedCur;
1470    const uint64_t     bCur = b->downloadedCur + b->uploadedCur;
1471
1472    if( aCur != bCur )
1473        return aCur > bCur ? -1 : 1; /* close the biggest torrents first */
1474
1475    return 0;
1476}
1477
1478static void closeBlocklists( tr_session * );
1479
1480static void
1481sessionCloseImpl( void * vsession )
1482{
1483    tr_session *  session = vsession;
1484    tr_torrent *  tor;
1485    int           i, n;
1486    tr_torrent ** torrents;
1487
1488    assert( tr_isSession( session ) );
1489
1490    free_incoming_peer_port( session );
1491
1492    if( session->isDHTEnabled )
1493        tr_dhtUninit( session );
1494
1495    evtimer_del( session->saveTimer );
1496    tr_free( session->saveTimer );
1497    session->saveTimer = NULL;
1498
1499    evtimer_del( session->nowTimer );
1500    tr_free( session->nowTimer );
1501    session->nowTimer = NULL;
1502
1503    tr_verifyClose( session );
1504    tr_sharedClose( session );
1505    tr_rpcClose( &session->rpcServer );
1506
1507    /* close the torrents.  get the most active ones first so that
1508     * if we can't get them all closed in a reasonable amount of time,
1509     * at least we get the most important ones first. */
1510    tor = NULL;
1511    n = session->torrentCount;
1512    torrents = tr_new( tr_torrent *, session->torrentCount );
1513    for( i = 0; i < n; ++i )
1514        torrents[i] = tor = tr_torrentNext( session, tor );
1515    qsort( torrents, n, sizeof( tr_torrent* ), compareTorrentByCur );
1516    for( i = 0; i < n; ++i )
1517        tr_torrentFree( torrents[i] );
1518    tr_free( torrents );
1519
1520    tr_announcerClose( session );
1521    tr_statsClose( session );
1522    tr_peerMgrFree( session->peerMgr );
1523    tr_webClose( session, TR_WEB_CLOSE_WHEN_IDLE );
1524
1525    closeBlocklists( session );
1526
1527    tr_fdClose( session );
1528
1529    session->isClosed = TRUE;
1530}
1531
1532static int
1533deadlineReached( const time_t deadline )
1534{
1535    return time( NULL ) >= deadline;
1536}
1537
1538#define SHUTDOWN_MAX_SECONDS 20
1539
1540void
1541tr_sessionClose( tr_session * session )
1542{
1543    const time_t deadline = time( NULL ) + SHUTDOWN_MAX_SECONDS;
1544
1545    assert( tr_isSession( session ) );
1546
1547    dbgmsg( "shutting down transmission session %p", session );
1548
1549    /* close the session */
1550    tr_runInEventThread( session, sessionCloseImpl, session );
1551    while( !session->isClosed && !deadlineReached( deadline ) )
1552    {
1553        dbgmsg( "waiting for the libtransmission thread to finish" );
1554        tr_wait_msec( 100 );
1555    }
1556
1557    /* "shared" and "tracker" have live sockets,
1558     * so we need to keep the transmission thread alive
1559     * for a bit while they tell the router & tracker
1560     * that we're closing now */
1561    while( ( session->shared || session->web || session->announcer )
1562           && !deadlineReached( deadline ) )
1563    {
1564        dbgmsg( "waiting on port unmap (%p) or announcer (%p)",
1565                session->shared, session->announcer );
1566        tr_wait_msec( 100 );
1567    }
1568
1569    tr_webClose( session, TR_WEB_CLOSE_NOW );
1570
1571    /* close the libtransmission thread */
1572    tr_eventClose( session );
1573    while( session->events != NULL )
1574    {
1575        static tr_bool forced = FALSE;
1576        dbgmsg( "waiting for libtransmission thread to finish" );
1577        tr_wait_msec( 500 );
1578        if( deadlineReached( deadline ) && !forced )
1579        {
1580            event_loopbreak( );
1581            forced = TRUE;
1582
1583            if( time( NULL ) >= deadline + 3 )
1584                break;
1585        }
1586    }
1587
1588    /* free the session memory */
1589    tr_bencFree( &session->removedTorrents );
1590    tr_bandwidthFree( session->bandwidth );
1591    tr_bitfieldDestruct( &session->turtle.minutes );
1592    tr_lockFree( session->lock );
1593    if( session->metainfoLookup ) {
1594        tr_bencFree( session->metainfoLookup );
1595        tr_free( session->metainfoLookup );
1596    }
1597    tr_free( session->buffer );
1598    tr_free( session->tag );
1599    tr_free( session->configDir );
1600    tr_free( session->resumeDir );
1601    tr_free( session->torrentDir );
1602    tr_free( session->downloadDir );
1603    tr_free( session->incompleteDir );
1604    tr_free( session->proxy );
1605    tr_free( session->proxyUsername );
1606    tr_free( session->proxyPassword );
1607    tr_free( session );
1608}
1609
1610tr_torrent **
1611tr_sessionLoadTorrents( tr_session * session,
1612                        tr_ctor    * ctor,
1613                        int        * setmeCount )
1614{
1615    int           i, n = 0;
1616    struct stat   sb;
1617    DIR *         odir = NULL;
1618    const char *  dirname = tr_getTorrentDir( session );
1619    tr_torrent ** torrents;
1620    tr_list *     l = NULL, *list = NULL;
1621
1622    assert( tr_isSession( session ) );
1623
1624    tr_ctorSetSave( ctor, FALSE ); /* since we already have them */
1625
1626    if( !stat( dirname, &sb )
1627      && S_ISDIR( sb.st_mode )
1628      && ( ( odir = opendir ( dirname ) ) ) )
1629    {
1630        struct dirent *d;
1631        for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
1632        {
1633            if( d->d_name && d->d_name[0] != '.' ) /* skip dotfiles, ., and ..
1634                                                     */
1635            {
1636                tr_torrent * tor;
1637                char * path = tr_buildPath( dirname, d->d_name, NULL );
1638                tr_ctorSetMetainfoFromFile( ctor, path );
1639                if(( tor = tr_torrentNew( ctor, NULL )))
1640                {
1641                    tr_list_append( &list, tor );
1642                    ++n;
1643                }
1644                tr_free( path );
1645            }
1646        }
1647        closedir( odir );
1648    }
1649
1650    torrents = tr_new( tr_torrent *, n );
1651    for( i = 0, l = list; l != NULL; l = l->next )
1652        torrents[i++] = (tr_torrent*) l->data;
1653    assert( i == n );
1654
1655    tr_list_free( &list, NULL );
1656
1657    if( n )
1658        tr_inf( _( "Loaded %d torrents" ), n );
1659
1660    if( setmeCount )
1661        *setmeCount = n;
1662    return torrents;
1663}
1664
1665/***
1666****
1667***/
1668
1669void
1670tr_sessionSetPexEnabled( tr_session * session,
1671                         tr_bool      enabled )
1672{
1673    assert( tr_isSession( session ) );
1674
1675    session->isPexEnabled = enabled != 0;
1676}
1677
1678tr_bool
1679tr_sessionIsPexEnabled( const tr_session * session )
1680{
1681    assert( tr_isSession( session ) );
1682
1683    return session->isPexEnabled;
1684}
1685
1686tr_bool
1687tr_sessionAllowsDHT( const tr_session * session UNUSED )
1688{
1689    return tr_sessionIsDHTEnabled( session );
1690}
1691
1692tr_bool
1693tr_sessionIsDHTEnabled( const tr_session * session )
1694{
1695    assert( tr_isSession( session ) );
1696
1697    return session->isDHTEnabled;
1698}
1699
1700static void
1701toggleDHTImpl(  void * data )
1702{
1703    tr_session * session = data;
1704    assert( tr_isSession( session ) );
1705
1706    if( session->isDHTEnabled )
1707        tr_dhtUninit( session );
1708
1709    session->isDHTEnabled = !session->isDHTEnabled;
1710
1711    if( session->isDHTEnabled )
1712        tr_dhtInit( session, &session->public_ipv4->addr );
1713}
1714
1715void
1716tr_sessionSetDHTEnabled( tr_session * session, tr_bool enabled )
1717{
1718    assert( tr_isSession( session ) );
1719    assert( tr_isBool( enabled ) );
1720
1721    if( ( enabled != 0 ) != ( session->isDHTEnabled != 0 ) )
1722        tr_runInEventThread( session, toggleDHTImpl, session );
1723}
1724
1725/***
1726****
1727***/
1728
1729void
1730tr_sessionSetLazyBitfieldEnabled( tr_session * session,
1731                                  tr_bool      enabled )
1732{
1733    assert( tr_isSession( session ) );
1734
1735    session->useLazyBitfield = enabled != 0;
1736}
1737
1738tr_bool
1739tr_sessionIsLazyBitfieldEnabled( const tr_session * session )
1740{
1741    assert( tr_isSession( session ) );
1742
1743    return session->useLazyBitfield;
1744}
1745
1746/***
1747****
1748***/
1749
1750struct port_forwarding_data
1751{
1752    tr_bool enabled;
1753    struct tr_shared * shared;
1754};
1755
1756static void
1757setPortForwardingEnabled( void * vdata )
1758{
1759    struct port_forwarding_data * data = vdata;
1760    tr_sharedTraversalEnable( data->shared, data->enabled );
1761    tr_free( data );
1762}
1763
1764void
1765tr_sessionSetPortForwardingEnabled( tr_session  * session, tr_bool enabled )
1766{
1767    struct port_forwarding_data * d;
1768    d = tr_new0( struct port_forwarding_data, 1 );
1769    d->shared = session->shared;
1770    d->enabled = enabled;
1771    tr_runInEventThread( session, setPortForwardingEnabled, d );
1772}
1773
1774tr_bool
1775tr_sessionIsPortForwardingEnabled( const tr_session * session )
1776{
1777    assert( tr_isSession( session ) );
1778
1779    return tr_sharedTraversalIsEnabled( session->shared );
1780}
1781
1782/***
1783****
1784***/
1785
1786static int
1787tr_stringEndsWith( const char * str, const char * end )
1788{
1789    const size_t slen = strlen( str );
1790    const size_t elen = strlen( end );
1791
1792    return slen >= elen && !memcmp( &str[slen - elen], end, elen );
1793}
1794
1795static void
1796loadBlocklists( tr_session * session )
1797{
1798    int         binCount = 0;
1799    int         newCount = 0;
1800    struct stat sb;
1801    char      * dirname;
1802    DIR *       odir = NULL;
1803    tr_list *   list = NULL;
1804    const tr_bool   isEnabled = session->isBlocklistEnabled;
1805
1806    /* walk through the directory and find blocklists */
1807    dirname = tr_buildPath( session->configDir, "blocklists", NULL );
1808    if( !stat( dirname,
1809               &sb ) && S_ISDIR( sb.st_mode )
1810      && ( ( odir = opendir( dirname ) ) ) )
1811    {
1812        struct dirent *d;
1813        for( d = readdir( odir ); d; d = readdir( odir ) )
1814        {
1815            char * filename;
1816
1817            if( !d->d_name || d->d_name[0] == '.' ) /* skip dotfiles, ., and ..
1818                                                      */
1819                continue;
1820
1821            filename = tr_buildPath( dirname, d->d_name, NULL );
1822
1823            if( tr_stringEndsWith( filename, ".bin" ) )
1824            {
1825                /* if we don't already have this blocklist, add it */
1826                if( !tr_list_find( list, filename,
1827                                   (TrListCompareFunc)strcmp ) )
1828                {
1829                    tr_list_append( &list,
1830                                   _tr_blocklistNew( filename, isEnabled ) );
1831                    ++binCount;
1832                }
1833            }
1834            else
1835            {
1836                /* strip out the file suffix, if there is one, and add ".bin"
1837                  instead */
1838                tr_blocklist * b;
1839                const char *   dot = strrchr( d->d_name, '.' );
1840                const int      len = dot ? dot - d->d_name
1841                                         : (int)strlen( d->d_name );
1842                char         * tmp = tr_strdup_printf(
1843                                        "%s" TR_PATH_DELIMITER_STR "%*.*s.bin",
1844                                        dirname, len, len, d->d_name );
1845                b = _tr_blocklistNew( tmp, isEnabled );
1846                _tr_blocklistSetContent( b, filename );
1847                tr_list_append( &list, b );
1848                ++newCount;
1849                tr_free( tmp );
1850            }
1851
1852            tr_free( filename );
1853        }
1854
1855        closedir( odir );
1856    }
1857
1858    session->blocklists = list;
1859
1860    if( binCount )
1861        tr_dbg( "Found %d blocklists in \"%s\"", binCount, dirname );
1862    if( newCount )
1863        tr_dbg( "Found %d new blocklists in \"%s\"", newCount, dirname );
1864
1865    tr_free( dirname );
1866}
1867
1868static void
1869closeBlocklists( tr_session * session )
1870{
1871    tr_list_free( &session->blocklists,
1872                  (TrListForeachFunc)_tr_blocklistFree );
1873}
1874
1875void
1876tr_sessionReloadBlocklists( tr_session * session )
1877{
1878    closeBlocklists( session );
1879    loadBlocklists( session );
1880}
1881
1882int
1883tr_blocklistGetRuleCount( const tr_session * session )
1884{
1885    int       n = 0;
1886    tr_list * l;
1887
1888    assert( tr_isSession( session ) );
1889
1890    for( l = session->blocklists; l; l = l->next )
1891        n += _tr_blocklistGetRuleCount( l->data );
1892    return n;
1893}
1894
1895tr_bool
1896tr_blocklistIsEnabled( const tr_session * session )
1897{
1898    assert( tr_isSession( session ) );
1899
1900    return session->isBlocklistEnabled;
1901}
1902
1903void
1904tr_blocklistSetEnabled( tr_session * session,
1905                        tr_bool      isEnabled )
1906{
1907    tr_list * l;
1908
1909    assert( tr_isSession( session ) );
1910
1911    session->isBlocklistEnabled = isEnabled != 0;
1912
1913    for( l=session->blocklists; l!=NULL; l=l->next )
1914        _tr_blocklistSetEnabled( l->data, isEnabled );
1915}
1916
1917tr_bool
1918tr_blocklistExists( const tr_session * session )
1919{
1920    assert( tr_isSession( session ) );
1921
1922    return session->blocklists != NULL;
1923}
1924
1925int
1926tr_blocklistSetContent( tr_session * session,
1927                        const char * contentFilename )
1928{
1929    tr_list * l;
1930    int ruleCount;
1931    tr_blocklist * b;
1932    const char * defaultName = "level1.bin";
1933    tr_sessionLock( session );
1934
1935    for( b = NULL, l = session->blocklists; !b && l; l = l->next )
1936        if( tr_stringEndsWith( _tr_blocklistGetFilename( l->data ),
1937                               defaultName ) )
1938            b = l->data;
1939
1940    if( !b )
1941    {
1942        char * path = tr_buildPath( session->configDir, "blocklists", defaultName, NULL );
1943        b = _tr_blocklistNew( path, session->isBlocklistEnabled );
1944        tr_list_append( &session->blocklists, b );
1945        tr_free( path );
1946    }
1947
1948    ruleCount = _tr_blocklistSetContent( b, contentFilename );
1949    tr_sessionUnlock( session );
1950    return ruleCount;
1951}
1952
1953tr_bool
1954tr_sessionIsAddressBlocked( const tr_session * session,
1955                            const tr_address * addr )
1956{
1957    tr_list * l;
1958
1959    assert( tr_isSession( session ) );
1960
1961    for( l = session->blocklists; l; l = l->next )
1962        if( _tr_blocklistHasAddress( l->data, addr ) )
1963            return TRUE;
1964    return FALSE;
1965}
1966
1967/***
1968****
1969***/
1970
1971static void
1972metainfoLookupInit( tr_session * session )
1973{
1974    struct stat  sb;
1975    const char * dirname = tr_getTorrentDir( session );
1976    DIR *        odir = NULL;
1977    tr_ctor *    ctor = NULL;
1978    tr_benc * lookup;
1979    int n = 0;
1980
1981    assert( tr_isSession( session ) );
1982
1983    /* walk through the directory and find the mappings */
1984    lookup = tr_new0( tr_benc, 1 );
1985    tr_bencInitDict( lookup, 0 );
1986    ctor = tr_ctorNew( session );
1987    tr_ctorSetSave( ctor, FALSE ); /* since we already have them */
1988    if( !stat( dirname, &sb ) && S_ISDIR( sb.st_mode ) && ( ( odir = opendir( dirname ) ) ) )
1989    {
1990        struct dirent *d;
1991        while(( d = readdir( odir )))
1992        {
1993            if( d->d_name && d->d_name[0] != '.' )
1994            {
1995                tr_info inf;
1996                char * path = tr_buildPath( dirname, d->d_name, NULL );
1997                tr_ctorSetMetainfoFromFile( ctor, path );
1998                if( !tr_torrentParse( ctor, &inf ) )
1999                {
2000                    ++n;
2001                    tr_bencDictAddStr( lookup, inf.hashString, path );
2002                }
2003                tr_free( path );
2004            }
2005        }
2006        closedir( odir );
2007    }
2008    tr_ctorFree( ctor );
2009
2010    session->metainfoLookup = lookup;
2011    tr_dbg( "Found %d torrents in \"%s\"", n, dirname );
2012}
2013
2014const char*
2015tr_sessionFindTorrentFile( const tr_session * session,
2016                           const char       * hashString )
2017{
2018    const char * filename = NULL;
2019    if( !session->metainfoLookup )
2020        metainfoLookupInit( (tr_session*)session );
2021    tr_bencDictFindStr( session->metainfoLookup, hashString, &filename );
2022    return filename;
2023}
2024
2025void
2026tr_sessionSetTorrentFile( tr_session * session,
2027                          const char * hashString,
2028                          const char * filename )
2029{
2030    /* since we walk session->configDir/torrents/ to build the lookup table,
2031     * and tr_sessionSetTorrentFile() is just to tell us there's a new file
2032     * in that same directory, we don't need to do anything here if the
2033     * lookup table hasn't been built yet */
2034    if( session->metainfoLookup )
2035        tr_bencDictAddStr( session->metainfoLookup, hashString, filename );
2036}
2037
2038tr_torrent*
2039tr_torrentNext( tr_session * session,
2040                tr_torrent * tor )
2041{
2042    tr_torrent * ret;
2043
2044    assert( !session || tr_isSession( session ) );
2045
2046    if( !session )
2047        ret = NULL;
2048    else if( !tor )
2049        ret = session->torrentList;
2050    else
2051        ret = tor->next;
2052
2053    return ret;
2054}
2055
2056/***
2057****
2058***/
2059
2060void
2061tr_sessionSetRPCEnabled( tr_session * session,
2062                         tr_bool      isEnabled )
2063{
2064    assert( tr_isSession( session ) );
2065
2066    tr_rpcSetEnabled( session->rpcServer, isEnabled );
2067}
2068
2069tr_bool
2070tr_sessionIsRPCEnabled( const tr_session * session )
2071{
2072    assert( tr_isSession( session ) );
2073
2074    return tr_rpcIsEnabled( session->rpcServer );
2075}
2076
2077void
2078tr_sessionSetRPCPort( tr_session * session,
2079                      tr_port      port )
2080{
2081    assert( tr_isSession( session ) );
2082
2083    tr_rpcSetPort( session->rpcServer, port );
2084}
2085
2086tr_port
2087tr_sessionGetRPCPort( const tr_session * session )
2088{
2089    assert( tr_isSession( session ) );
2090
2091    return tr_rpcGetPort( session->rpcServer );
2092}
2093
2094void
2095tr_sessionSetRPCCallback( tr_session * session,
2096                          tr_rpc_func  func,
2097                          void *       user_data )
2098{
2099    assert( tr_isSession( session ) );
2100
2101    session->rpc_func = func;
2102    session->rpc_func_user_data = user_data;
2103}
2104
2105void
2106tr_sessionSetRPCWhitelist( tr_session * session,
2107                           const char * whitelist )
2108{
2109    assert( tr_isSession( session ) );
2110
2111    tr_rpcSetWhitelist( session->rpcServer, whitelist );
2112}
2113
2114const char*
2115tr_sessionGetRPCWhitelist( const tr_session * session )
2116{
2117    assert( tr_isSession( session ) );
2118
2119    return tr_rpcGetWhitelist( session->rpcServer );
2120}
2121
2122void
2123tr_sessionSetRPCWhitelistEnabled( tr_session * session,
2124                                  tr_bool      isEnabled )
2125{
2126    assert( tr_isSession( session ) );
2127
2128    tr_rpcSetWhitelistEnabled( session->rpcServer, isEnabled );
2129}
2130
2131tr_bool
2132tr_sessionGetRPCWhitelistEnabled( const tr_session * session )
2133{
2134    assert( tr_isSession( session ) );
2135
2136    return tr_rpcGetWhitelistEnabled( session->rpcServer );
2137}
2138
2139
2140void
2141tr_sessionSetRPCPassword( tr_session * session,
2142                          const char * password )
2143{
2144    assert( tr_isSession( session ) );
2145
2146    tr_rpcSetPassword( session->rpcServer, password );
2147}
2148
2149const char*
2150tr_sessionGetRPCPassword( const tr_session * session )
2151{
2152    assert( tr_isSession( session ) );
2153
2154    return tr_rpcGetPassword( session->rpcServer );
2155}
2156
2157void
2158tr_sessionSetRPCUsername( tr_session * session,
2159                          const char * username )
2160{
2161    assert( tr_isSession( session ) );
2162
2163    tr_rpcSetUsername( session->rpcServer, username );
2164}
2165
2166const char*
2167tr_sessionGetRPCUsername( const tr_session * session )
2168{
2169    assert( tr_isSession( session ) );
2170
2171    return tr_rpcGetUsername( session->rpcServer );
2172}
2173
2174void
2175tr_sessionSetRPCPasswordEnabled( tr_session * session,
2176                                 tr_bool      isEnabled )
2177{
2178    assert( tr_isSession( session ) );
2179
2180    tr_rpcSetPasswordEnabled( session->rpcServer, isEnabled );
2181}
2182
2183tr_bool
2184tr_sessionIsRPCPasswordEnabled( const tr_session * session )
2185{
2186    assert( tr_isSession( session ) );
2187
2188    return tr_rpcIsPasswordEnabled( session->rpcServer );
2189}
2190
2191const char *
2192tr_sessionGetRPCBindAddress( const tr_session * session )
2193{
2194    assert( tr_isSession( session ) );
2195
2196    return tr_rpcGetBindAddress( session->rpcServer );
2197}
2198
2199/***
2200****
2201***/
2202
2203tr_bool
2204tr_sessionIsProxyEnabled( const tr_session * session )
2205{
2206    assert( tr_isSession( session ) );
2207
2208    return session->isProxyEnabled;
2209}
2210
2211void
2212tr_sessionSetProxyEnabled( tr_session * session,
2213                           tr_bool      isEnabled )
2214{
2215    assert( tr_isSession( session ) );
2216    assert( tr_isBool( isEnabled ) );
2217
2218    session->isProxyEnabled = isEnabled != 0;
2219}
2220
2221tr_proxy_type
2222tr_sessionGetProxyType( const tr_session * session )
2223{
2224    assert( tr_isSession( session ) );
2225
2226    return session->proxyType;
2227}
2228
2229void
2230tr_sessionSetProxyType( tr_session *  session,
2231                        tr_proxy_type type )
2232{
2233    assert( tr_isSession( session ) );
2234
2235    session->proxyType = type;
2236}
2237
2238const char*
2239tr_sessionGetProxy( const tr_session * session )
2240{
2241    assert( tr_isSession( session ) );
2242
2243    return session->proxy;
2244}
2245
2246tr_port
2247tr_sessionGetProxyPort( const tr_session * session )
2248{
2249    assert( tr_isSession( session ) );
2250
2251    return session->proxyPort;
2252}
2253
2254void
2255tr_sessionSetProxy( tr_session * session,
2256                    const char * proxy )
2257{
2258    assert( tr_isSession( session ) );
2259
2260    if( proxy != session->proxy )
2261    {
2262        tr_free( session->proxy );
2263        session->proxy = tr_strdup( proxy );
2264    }
2265}
2266
2267void
2268tr_sessionSetProxyPort( tr_session * session,
2269                        tr_port      port )
2270{
2271    assert( tr_isSession( session ) );
2272
2273    session->proxyPort = port;
2274}
2275
2276tr_bool
2277tr_sessionIsProxyAuthEnabled( const tr_session * session )
2278{
2279    assert( tr_isSession( session ) );
2280
2281    return session->isProxyAuthEnabled;
2282}
2283
2284void
2285tr_sessionSetProxyAuthEnabled( tr_session * session,
2286                               tr_bool      isEnabled )
2287{
2288    assert( tr_isSession( session ) );
2289    assert( tr_isBool( isEnabled ) );
2290
2291    session->isProxyAuthEnabled = isEnabled != 0;
2292}
2293
2294const char*
2295tr_sessionGetProxyUsername( const tr_session * session )
2296{
2297    assert( tr_isSession( session ) );
2298
2299    return session->proxyUsername;
2300}
2301
2302void
2303tr_sessionSetProxyUsername( tr_session * session,
2304                            const char * username )
2305{
2306    assert( tr_isSession( session ) );
2307
2308    if( username != session->proxyUsername )
2309    {
2310        tr_free( session->proxyUsername );
2311        session->proxyUsername = tr_strdup( username );
2312    }
2313}
2314
2315const char*
2316tr_sessionGetProxyPassword( const tr_session * session )
2317{
2318    assert( tr_isSession( session ) );
2319
2320    return session->proxyPassword;
2321}
2322
2323void
2324tr_sessionSetProxyPassword( tr_session * session,
2325                            const char * password )
2326{
2327    assert( tr_isSession( session ) );
2328
2329    if( password != session->proxyPassword )
2330    {
2331        tr_free( session->proxyPassword );
2332        session->proxyPassword = tr_strdup( password );
2333    }
2334}
2335
2336int
2337tr_sessionGetActiveTorrentCount( tr_session * session )
2338{
2339    int ret = 0;
2340    tr_torrent * tor = NULL;
2341
2342    assert( tr_isSession( session ) );
2343
2344    while(( tor = tr_torrentNext( session, tor )))
2345        if( tr_torrentGetActivity( tor ) != TR_STATUS_STOPPED )
2346            ++ret;
2347
2348    return ret;
2349}
Note: See TracBrowser for help on using the repository browser.