source: trunk/libtransmission/session.c @ 10736

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

(trunk) #3262 "problems with '.' as the first character in a .torrent's filename" -- fixed in trunk for 2.00

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