source: trunk/libtransmission/session.c @ 10124

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

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