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

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

(1.9x libT) backport r10538 for #3078 "transmission closes socket after SIGHUP"

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