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

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

(1.9x libT) backport r10352 and r10366 for #3026 "Speed Limit mode not honoring limits on other days"

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