source: trunk/libtransmission/session.c @ 10508

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

(trunk libT) #3162 "allow optional end-user configuration of TCP_CONGESTION" -- add jch's implementation of this to trunk for 2.00

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