source: trunk/libtransmission/session.c @ 10635

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

(trunk) #3060 "Local Peer Discovery" -- in the code, rename LDS as LPD for Local Peer Discovery

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