source: trunk/libtransmission/session.c @ 10390

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

(trunk) one of the least interesting commits ever: remove trailing spaces from source code lines...

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