source: trunk/libtransmission/session.c @ 10485

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

(trunk) #3141 "Web client should be disabled by default" -- implemented in trunk for 2.0

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