source: branches/1.9x/libtransmission/session.c @ 10601

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

(1.9x libT) backport r10511 for #3126 "Incomplete Dir. doesn't need to be auto-created". This patch makes the other clients behave as the mac client wrt using the download dir as the default incomplete dir

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