source: branches/2.0x/libtransmission/session.c @ 11005

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

(2.0x libT) #3423 "crash when saving prefs" -- fixed

  • Property svn:keywords set to Date Rev Author Id
File size: 68.5 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 11005 2010-07-13 13:35:58Z 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    if( tr_isSession( session ) && ( session->peerPort != port ) )
1003    {
1004        setPeerPort( session, port );
1005    }
1006}
1007
1008tr_port
1009tr_sessionGetPeerPort( const tr_session * session )
1010{
1011    return tr_isSession( session ) ? session->peerPort : 0;
1012}
1013
1014tr_port
1015tr_sessionSetPeerPortRandom( tr_session * session )
1016{
1017    assert( tr_isSession( session ) );
1018
1019    tr_sessionSetPeerPort( session, getRandomPort( session ) );
1020    return session->peerPort;
1021}
1022
1023void
1024tr_sessionSetPeerPortRandomOnStart( tr_session * session,
1025                                    tr_bool random )
1026{
1027    assert( tr_isSession( session ) );
1028
1029    session->isPortRandom = random;
1030}
1031
1032tr_bool
1033tr_sessionGetPeerPortRandomOnStart( tr_session * session )
1034{
1035    assert( tr_isSession( session ) );
1036
1037    return session->isPortRandom;
1038}
1039
1040tr_port_forwarding
1041tr_sessionGetPortForwarding( const tr_session * session )
1042{
1043    assert( tr_isSession( session ) );
1044
1045    return tr_sharedTraversalStatus( session->shared );
1046}
1047
1048/***
1049****
1050***/
1051
1052void
1053tr_sessionSetRatioLimited( tr_session * session, tr_bool isLimited )
1054{
1055    assert( tr_isSession( session ) );
1056
1057    session->isRatioLimited = isLimited;
1058}
1059
1060void
1061tr_sessionSetRatioLimit( tr_session * session, double desiredRatio )
1062{
1063    assert( tr_isSession( session ) );
1064
1065    session->desiredRatio = desiredRatio;
1066}
1067
1068tr_bool
1069tr_sessionIsRatioLimited( const tr_session  * session )
1070{
1071    assert( tr_isSession( session ) );
1072
1073    return session->isRatioLimited;
1074}
1075
1076double
1077tr_sessionGetRatioLimit( const tr_session * session )
1078{
1079    assert( tr_isSession( session ) );
1080
1081    return session->desiredRatio;
1082}
1083
1084/***
1085****
1086****  SPEED LIMITS
1087****
1088***/
1089
1090tr_bool
1091tr_sessionGetActiveSpeedLimit( const tr_session * session, tr_direction dir, int * setme )
1092{
1093    int isLimited = TRUE;
1094
1095    if( !tr_isSession( session ) )
1096        return FALSE;
1097
1098    if( tr_sessionUsesAltSpeed( session ) )
1099        *setme = tr_sessionGetAltSpeed( session, dir );
1100    else if( tr_sessionIsSpeedLimited( session, dir ) )
1101        *setme = tr_sessionGetSpeedLimit( session, dir );
1102    else
1103        isLimited = FALSE;
1104
1105    return isLimited;
1106}
1107
1108static void
1109updateBandwidth( tr_session * session, tr_direction dir )
1110{
1111    int limit = 0;
1112    const tr_bool isLimited = tr_sessionGetActiveSpeedLimit( session, dir, &limit );
1113    const tr_bool zeroCase = isLimited && !limit;
1114
1115    tr_bandwidthSetLimited( session->bandwidth, dir, isLimited && !zeroCase );
1116
1117    tr_bandwidthSetDesiredSpeed( session->bandwidth, dir, limit );
1118}
1119
1120enum
1121{
1122    MINUTES_PER_HOUR = 60,
1123    MINUTES_PER_DAY = MINUTES_PER_HOUR * 24,
1124    MINUTES_PER_WEEK = MINUTES_PER_DAY * 7
1125};
1126
1127static void
1128turtleUpdateTable( struct tr_turtle_info * t )
1129{
1130    int day;
1131    tr_bitfield * b = &t->minutes;
1132
1133    tr_bitfieldClear( b );
1134
1135    for( day=0; day<7; ++day )
1136    {
1137        if( t->days & (1<<day) )
1138        {
1139            int i;
1140            const time_t begin = t->beginMinute;
1141            time_t end = t->endMinute;
1142
1143            if( end <= begin )
1144                end += MINUTES_PER_DAY;
1145
1146            for( i=begin; i<end; ++i )
1147                tr_bitfieldAdd( b, (i+day*MINUTES_PER_DAY) % MINUTES_PER_WEEK );
1148        }
1149    }
1150}
1151
1152static void
1153altSpeedToggled( void * vsession )
1154{
1155    tr_session * session = vsession;
1156    struct tr_turtle_info * t = &session->turtle;
1157
1158    assert( tr_isSession( session ) );
1159
1160    updateBandwidth( session, TR_UP );
1161    updateBandwidth( session, TR_DOWN );
1162
1163    if( t->callback != NULL )
1164        (*t->callback)( session, t->isEnabled, t->changedByUser, t->callbackUserData );
1165}
1166
1167static void
1168useAltSpeed( tr_session * s, struct tr_turtle_info * t,
1169             tr_bool enabled, tr_bool byUser )
1170{
1171    assert( tr_isSession( s ) );
1172    assert( t != NULL );
1173    assert( tr_isBool( enabled ) );
1174    assert( tr_isBool( byUser ) );
1175
1176    if( t->isEnabled != enabled )
1177    {
1178        t->isEnabled = enabled;
1179        t->changedByUser = byUser;
1180        tr_runInEventThread( s, altSpeedToggled, s );
1181    }
1182}
1183
1184/**
1185 * @param enabled whether turtle should be on/off according to the scheduler
1186 * @param changed whether that's different from the previous minute
1187 */
1188static void
1189testTurtleTime( const struct tr_turtle_info * t,
1190                tr_bool * enabled,
1191                tr_bool * changed )
1192{
1193    tr_bool e;
1194    struct tm tm;
1195    size_t minute_of_the_week;
1196    const time_t now = tr_time( );
1197
1198    tr_localtime_r( &now, &tm );
1199
1200    minute_of_the_week = tm.tm_wday * MINUTES_PER_DAY
1201                       + tm.tm_hour * MINUTES_PER_HOUR
1202                       + tm.tm_min;
1203    if( minute_of_the_week >= MINUTES_PER_WEEK ) /* leap minutes? */
1204        minute_of_the_week = MINUTES_PER_WEEK - 1;
1205
1206    e = tr_bitfieldHasFast( &t->minutes, minute_of_the_week );
1207    if( enabled != NULL )
1208        *enabled = e;
1209
1210    if( changed != NULL )
1211    {
1212        const size_t prev = minute_of_the_week > 0 ? minute_of_the_week - 1
1213                                                   : MINUTES_PER_WEEK - 1;
1214        *changed = e != tr_bitfieldHasFast( &t->minutes, prev );
1215    }
1216}
1217
1218static void
1219turtleCheckClock( tr_session * s, struct tr_turtle_info * t )
1220{
1221    tr_bool enabled;
1222    tr_bool changed;
1223
1224    assert( t->isClockEnabled );
1225
1226    testTurtleTime( t, &enabled, &changed );
1227
1228    if( changed )
1229    {
1230        tr_inf( "Time to turn %s turtle mode!", (enabled?"on":"off") );
1231        useAltSpeed( s, t, enabled, FALSE );
1232    }
1233}
1234
1235/* Called after the turtle's fields are loaded from an outside source.
1236 * It initializes the implementation fields
1237 * and turns on turtle mode if the clock settings say to. */
1238static void
1239turtleBootstrap( tr_session * session, struct tr_turtle_info * turtle )
1240{
1241    turtle->changedByUser = FALSE;
1242
1243    tr_bitfieldConstruct( &turtle->minutes, MINUTES_PER_WEEK );
1244
1245    turtleUpdateTable( turtle );
1246
1247    if( turtle->isClockEnabled )
1248        testTurtleTime( turtle, &turtle->isEnabled, NULL );
1249
1250    altSpeedToggled( session );
1251}
1252
1253/***
1254****  Primary session speed limits
1255***/
1256
1257void
1258tr_sessionSetSpeedLimit( tr_session * s, tr_direction d, int KB_s )
1259{
1260    assert( tr_isSession( s ) );
1261    assert( tr_isDirection( d ) );
1262    assert( KB_s >= 0 );
1263
1264    s->speedLimit[d] = KB_s;
1265
1266    updateBandwidth( s, d );
1267}
1268
1269int
1270tr_sessionGetSpeedLimit( const tr_session * s, tr_direction d )
1271{
1272    assert( tr_isSession( s ) );
1273    assert( tr_isDirection( d ) );
1274
1275    return s->speedLimit[d];
1276}
1277
1278void
1279tr_sessionLimitSpeed( tr_session * s, tr_direction d, tr_bool b )
1280{
1281    assert( tr_isSession( s ) );
1282    assert( tr_isDirection( d ) );
1283    assert( tr_isBool( b ) );
1284
1285    s->speedLimitEnabled[d] = b;
1286
1287    updateBandwidth( s, d );
1288}
1289
1290tr_bool
1291tr_sessionIsSpeedLimited( const tr_session * s, tr_direction d )
1292{
1293    assert( tr_isSession( s ) );
1294    assert( tr_isDirection( d ) );
1295
1296    return s->speedLimitEnabled[d];
1297}
1298
1299/***
1300****  Alternative speed limits that are used during scheduled times
1301***/
1302
1303void
1304tr_sessionSetAltSpeed( tr_session * s, tr_direction d, int KB_s )
1305{
1306    assert( tr_isSession( s ) );
1307    assert( tr_isDirection( d ) );
1308    assert( KB_s >= 0 );
1309
1310    s->turtle.speedLimit[d] = KB_s;
1311
1312    updateBandwidth( s, d );
1313}
1314
1315int
1316tr_sessionGetAltSpeed( const tr_session * s, tr_direction d )
1317{
1318    assert( tr_isSession( s ) );
1319    assert( tr_isDirection( d ) );
1320
1321    return s->turtle.speedLimit[d];
1322}
1323
1324static void
1325userPokedTheClock( tr_session * s, struct tr_turtle_info * t )
1326{
1327    tr_dbg( "Refreshing the turtle mode clock due to user changes" );
1328
1329    turtleUpdateTable( t );
1330
1331    if( t->isClockEnabled )
1332    {
1333        tr_bool enabled, changed;
1334        testTurtleTime( t, &enabled, &changed );
1335        useAltSpeed( s, t, enabled, TRUE );
1336    }
1337}
1338
1339void
1340tr_sessionUseAltSpeedTime( tr_session * s, tr_bool b )
1341{
1342    struct tr_turtle_info * t = &s->turtle;
1343
1344    assert( tr_isSession( s ) );
1345    assert( tr_isBool ( b ) );
1346
1347    if( t->isClockEnabled != b ) {
1348        t->isClockEnabled = b;
1349        userPokedTheClock( s, t );
1350    }
1351}
1352
1353tr_bool
1354tr_sessionUsesAltSpeedTime( const tr_session * s )
1355{
1356    assert( tr_isSession( s ) );
1357
1358    return s->turtle.isClockEnabled;
1359}
1360
1361void
1362tr_sessionSetAltSpeedBegin( tr_session * s, int minute )
1363{
1364    assert( tr_isSession( s ) );
1365    assert( 0<=minute && minute<(60*24) );
1366
1367    if( s->turtle.beginMinute != minute ) {
1368        s->turtle.beginMinute = minute;
1369        userPokedTheClock( s, &s->turtle );
1370    }
1371}
1372
1373int
1374tr_sessionGetAltSpeedBegin( const tr_session * s )
1375{
1376    assert( tr_isSession( s ) );
1377
1378    return s->turtle.beginMinute;
1379}
1380
1381void
1382tr_sessionSetAltSpeedEnd( tr_session * s, int minute )
1383{
1384    assert( tr_isSession( s ) );
1385    assert( 0<=minute && minute<(60*24) );
1386
1387    if( s->turtle.endMinute != minute ) {
1388        s->turtle.endMinute = minute;
1389        userPokedTheClock( s, &s->turtle );
1390    }
1391}
1392
1393int
1394tr_sessionGetAltSpeedEnd( const tr_session * s )
1395{
1396    assert( tr_isSession( s ) );
1397
1398    return s->turtle.endMinute;
1399}
1400
1401void
1402tr_sessionSetAltSpeedDay( tr_session * s, tr_sched_day days )
1403{
1404    assert( tr_isSession( s ) );
1405
1406    if( s->turtle.days != days ) {
1407        s->turtle.days = days;
1408        userPokedTheClock( s, &s->turtle );
1409    }
1410}
1411
1412tr_sched_day
1413tr_sessionGetAltSpeedDay( const tr_session * s )
1414{
1415    assert( tr_isSession( s ) );
1416
1417    return s->turtle.days;
1418}
1419
1420void
1421tr_sessionUseAltSpeed( tr_session * session, tr_bool enabled )
1422{
1423    useAltSpeed( session, &session->turtle, enabled, TRUE );
1424}
1425
1426tr_bool
1427tr_sessionUsesAltSpeed( const tr_session * s )
1428{
1429    assert( tr_isSession( s ) );
1430
1431    return s->turtle.isEnabled;
1432}
1433
1434void
1435tr_sessionSetAltSpeedFunc( tr_session       * session,
1436                           tr_altSpeedFunc    func,
1437                           void             * userData )
1438{
1439    assert( tr_isSession( session ) );
1440
1441    session->turtle.callback = func;
1442    session->turtle.callbackUserData = userData;
1443}
1444
1445void
1446tr_sessionClearAltSpeedFunc( tr_session * session )
1447{
1448    tr_sessionSetAltSpeedFunc( session, NULL, NULL );
1449}
1450
1451/***
1452****
1453***/
1454
1455void
1456tr_sessionSetPeerLimit( tr_session * session, uint16_t maxGlobalPeers )
1457{
1458    assert( tr_isSession( session ) );
1459
1460    tr_fdSetPeerLimit( session, maxGlobalPeers );
1461}
1462
1463uint16_t
1464tr_sessionGetPeerLimit( const tr_session * session )
1465{
1466    assert( tr_isSession( session ) );
1467
1468    return tr_fdGetPeerLimit( session );
1469}
1470
1471void
1472tr_sessionSetPeerLimitPerTorrent( tr_session  * session, uint16_t n )
1473{
1474    assert( tr_isSession( session ) );
1475
1476    session->peerLimitPerTorrent = n;
1477}
1478
1479uint16_t
1480tr_sessionGetPeerLimitPerTorrent( const tr_session * session )
1481{
1482    assert( tr_isSession( session ) );
1483
1484    return session->peerLimitPerTorrent;
1485}
1486
1487/***
1488****
1489***/
1490
1491void
1492tr_sessionSetPaused( tr_session * session, tr_bool isPaused )
1493{
1494    assert( tr_isSession( session ) );
1495
1496    session->pauseAddedTorrent = isPaused;
1497}
1498
1499tr_bool
1500tr_sessionGetPaused( const tr_session * session )
1501{
1502    assert( tr_isSession( session ) );
1503
1504    return session->pauseAddedTorrent;
1505}
1506
1507void
1508tr_sessionSetDeleteSource( tr_session * session, tr_bool deleteSource )
1509{
1510    assert( tr_isSession( session ) );
1511
1512    session->deleteSourceTorrent = deleteSource;
1513}
1514
1515tr_bool
1516tr_sessionGetDeleteSource( const tr_session * session )
1517{
1518    assert( tr_isSession( session ) );
1519
1520    return session->deleteSourceTorrent;
1521}
1522
1523/***
1524****
1525***/
1526
1527double
1528tr_sessionGetPieceSpeed( const tr_session * session, tr_direction dir )
1529{
1530    return tr_isSession( session ) ? tr_bandwidthGetPieceSpeed( session->bandwidth, 0, dir ) : 0.0;
1531}
1532
1533double
1534tr_sessionGetRawSpeed( const tr_session * session, tr_direction dir )
1535{
1536    return tr_isSession( session ) ? tr_bandwidthGetRawSpeed( session->bandwidth, 0, dir ) : 0.0;
1537}
1538
1539int
1540tr_sessionCountTorrents( const tr_session * session )
1541{
1542    return tr_isSession( session ) ? session->torrentCount : 0;
1543}
1544
1545static int
1546compareTorrentByCur( const void * va, const void * vb )
1547{
1548    const tr_torrent * a = *(const tr_torrent**)va;
1549    const tr_torrent * b = *(const tr_torrent**)vb;
1550    const uint64_t     aCur = a->downloadedCur + a->uploadedCur;
1551    const uint64_t     bCur = b->downloadedCur + b->uploadedCur;
1552
1553    if( aCur != bCur )
1554        return aCur > bCur ? -1 : 1; /* close the biggest torrents first */
1555
1556    return 0;
1557}
1558
1559static void closeBlocklists( tr_session * );
1560
1561static void
1562sessionCloseImpl( void * vsession )
1563{
1564    tr_session *  session = vsession;
1565    tr_torrent *  tor;
1566    int           i, n;
1567    tr_torrent ** torrents;
1568
1569    assert( tr_isSession( session ) );
1570
1571    free_incoming_peer_port( session );
1572
1573    if( session->isLPDEnabled )
1574        tr_lpdUninit( session );
1575
1576    if( session->isDHTEnabled )
1577        tr_dhtUninit( session );
1578
1579    evtimer_del( session->saveTimer );
1580    tr_free( session->saveTimer );
1581    session->saveTimer = NULL;
1582
1583    evtimer_del( session->nowTimer );
1584    tr_free( session->nowTimer );
1585    session->nowTimer = NULL;
1586
1587    tr_verifyClose( session );
1588    tr_sharedClose( session );
1589    tr_rpcClose( &session->rpcServer );
1590
1591    /* close the torrents.  get the most active ones first so that
1592     * if we can't get them all closed in a reasonable amount of time,
1593     * at least we get the most important ones first. */
1594    tor = NULL;
1595    n = session->torrentCount;
1596    torrents = tr_new( tr_torrent *, session->torrentCount );
1597    for( i = 0; i < n; ++i )
1598        torrents[i] = tor = tr_torrentNext( session, tor );
1599    qsort( torrents, n, sizeof( tr_torrent* ), compareTorrentByCur );
1600    for( i = 0; i < n; ++i )
1601        tr_torrentFree( torrents[i] );
1602    tr_free( torrents );
1603
1604    tr_announcerClose( session );
1605    tr_statsClose( session );
1606    tr_peerMgrFree( session->peerMgr );
1607    tr_webClose( session, TR_WEB_CLOSE_WHEN_IDLE );
1608
1609    closeBlocklists( session );
1610
1611    tr_fdClose( session );
1612
1613    session->isClosed = TRUE;
1614}
1615
1616static int
1617deadlineReached( const time_t deadline )
1618{
1619    return time( NULL ) >= deadline;
1620}
1621
1622#define SHUTDOWN_MAX_SECONDS 20
1623
1624void
1625tr_sessionClose( tr_session * session )
1626{
1627    const time_t deadline = time( NULL ) + SHUTDOWN_MAX_SECONDS;
1628
1629    assert( tr_isSession( session ) );
1630
1631    dbgmsg( "shutting down transmission session %p", session );
1632
1633    /* close the session */
1634    tr_runInEventThread( session, sessionCloseImpl, session );
1635    while( !session->isClosed && !deadlineReached( deadline ) )
1636    {
1637        dbgmsg( "waiting for the libtransmission thread to finish" );
1638        tr_wait_msec( 100 );
1639    }
1640
1641    /* "shared" and "tracker" have live sockets,
1642     * so we need to keep the transmission thread alive
1643     * for a bit while they tell the router & tracker
1644     * that we're closing now */
1645    while( ( session->shared || session->web || session->announcer )
1646           && !deadlineReached( deadline ) )
1647    {
1648        dbgmsg( "waiting on port unmap (%p) or announcer (%p)",
1649                session->shared, session->announcer );
1650        tr_wait_msec( 100 );
1651    }
1652
1653    tr_webClose( session, TR_WEB_CLOSE_NOW );
1654
1655    /* close the libtransmission thread */
1656    tr_eventClose( session );
1657    while( session->events != NULL )
1658    {
1659        static tr_bool forced = FALSE;
1660        dbgmsg( "waiting for libtransmission thread to finish" );
1661        tr_wait_msec( 500 );
1662        if( deadlineReached( deadline ) && !forced )
1663        {
1664            event_loopbreak( );
1665            forced = TRUE;
1666
1667            if( time( NULL ) >= deadline + 3 )
1668                break;
1669        }
1670    }
1671
1672    /* free the session memory */
1673    tr_bencFree( &session->removedTorrents );
1674    tr_bandwidthFree( session->bandwidth );
1675    tr_bitfieldDestruct( &session->turtle.minutes );
1676    tr_lockFree( session->lock );
1677    if( session->metainfoLookup ) {
1678        tr_bencFree( session->metainfoLookup );
1679        tr_free( session->metainfoLookup );
1680    }
1681    tr_free( session->torrentDoneScript );
1682    tr_free( session->buffer );
1683    tr_free( session->tag );
1684    tr_free( session->configDir );
1685    tr_free( session->resumeDir );
1686    tr_free( session->torrentDir );
1687    tr_free( session->downloadDir );
1688    tr_free( session->incompleteDir );
1689    tr_free( session->proxy );
1690    tr_free( session->proxyUsername );
1691    tr_free( session->proxyPassword );
1692    tr_free( session->peer_congestion_algorithm );
1693    tr_free( session );
1694}
1695
1696tr_torrent **
1697tr_sessionLoadTorrents( tr_session * session,
1698                        tr_ctor    * ctor,
1699                        int        * setmeCount )
1700{
1701    int           i, n = 0;
1702    struct stat   sb;
1703    DIR *         odir = NULL;
1704    const char *  dirname = tr_getTorrentDir( session );
1705    tr_torrent ** torrents;
1706    tr_list *     l = NULL, *list = NULL;
1707
1708    assert( tr_isSession( session ) );
1709
1710    tr_ctorSetSave( ctor, FALSE ); /* since we already have them */
1711
1712    if( !stat( dirname, &sb )
1713      && S_ISDIR( sb.st_mode )
1714      && ( ( odir = opendir ( dirname ) ) ) )
1715    {
1716        struct dirent *d;
1717        for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
1718        {
1719            if( tr_str_has_suffix( d->d_name, ".torrent" ) )
1720            {
1721                tr_torrent * tor;
1722                char * path = tr_buildPath( dirname, d->d_name, NULL );
1723                tr_ctorSetMetainfoFromFile( ctor, path );
1724                if(( tor = tr_torrentNew( ctor, NULL )))
1725                {
1726                    tr_list_append( &list, tor );
1727                    ++n;
1728                }
1729                tr_free( path );
1730            }
1731        }
1732        closedir( odir );
1733    }
1734
1735    torrents = tr_new( tr_torrent *, n );
1736    for( i = 0, l = list; l != NULL; l = l->next )
1737        torrents[i++] = (tr_torrent*) l->data;
1738    assert( i == n );
1739
1740    tr_list_free( &list, NULL );
1741
1742    if( n )
1743        tr_inf( _( "Loaded %d torrents" ), n );
1744
1745    if( setmeCount )
1746        *setmeCount = n;
1747    return torrents;
1748}
1749
1750/***
1751****
1752***/
1753
1754void
1755tr_sessionSetPexEnabled( tr_session * session,
1756                         tr_bool      enabled )
1757{
1758    assert( tr_isSession( session ) );
1759
1760    session->isPexEnabled = enabled != 0;
1761}
1762
1763tr_bool
1764tr_sessionIsPexEnabled( const tr_session * session )
1765{
1766    assert( tr_isSession( session ) );
1767
1768    return session->isPexEnabled;
1769}
1770
1771tr_bool
1772tr_sessionAllowsDHT( const tr_session * session UNUSED )
1773{
1774    return tr_sessionIsDHTEnabled( session );
1775}
1776
1777tr_bool
1778tr_sessionIsDHTEnabled( const tr_session * session )
1779{
1780    assert( tr_isSession( session ) );
1781
1782    return session->isDHTEnabled;
1783}
1784
1785static void
1786toggleDHTImpl(  void * data )
1787{
1788    tr_session * session = data;
1789    assert( tr_isSession( session ) );
1790
1791    if( session->isDHTEnabled )
1792        tr_dhtUninit( session );
1793
1794    session->isDHTEnabled = !session->isDHTEnabled;
1795
1796    if( session->isDHTEnabled )
1797        tr_dhtInit( session, &session->public_ipv4->addr );
1798}
1799
1800void
1801tr_sessionSetDHTEnabled( tr_session * session, tr_bool enabled )
1802{
1803    assert( tr_isSession( session ) );
1804    assert( tr_isBool( enabled ) );
1805
1806    if( ( enabled != 0 ) != ( session->isDHTEnabled != 0 ) )
1807        tr_runInEventThread( session, toggleDHTImpl, session );
1808}
1809
1810void
1811tr_sessionSetLPDEnabled( tr_session * session,
1812                         tr_bool      enabled )
1813{
1814    assert( tr_isSession( session ) );
1815
1816    session->isLPDEnabled = ( enabled != 0 );
1817}
1818
1819tr_bool
1820tr_sessionIsLPDEnabled( const tr_session * session )
1821{
1822    assert( tr_isSession( session ) );
1823
1824    return session->isLPDEnabled;
1825}
1826
1827tr_bool
1828tr_sessionAllowsLPD( const tr_session * session )
1829{
1830    return tr_sessionIsLPDEnabled( session );
1831}
1832
1833/***
1834****
1835***/
1836
1837void
1838tr_sessionSetLazyBitfieldEnabled( tr_session * session,
1839                                  tr_bool      enabled )
1840{
1841    assert( tr_isSession( session ) );
1842
1843    session->useLazyBitfield = enabled != 0;
1844}
1845
1846tr_bool
1847tr_sessionIsLazyBitfieldEnabled( const tr_session * session )
1848{
1849    assert( tr_isSession( session ) );
1850
1851    return session->useLazyBitfield;
1852}
1853
1854/***
1855****
1856***/
1857
1858struct port_forwarding_data
1859{
1860    tr_bool enabled;
1861    struct tr_shared * shared;
1862};
1863
1864static void
1865setPortForwardingEnabled( void * vdata )
1866{
1867    struct port_forwarding_data * data = vdata;
1868    tr_sharedTraversalEnable( data->shared, data->enabled );
1869    tr_free( data );
1870}
1871
1872void
1873tr_sessionSetPortForwardingEnabled( tr_session  * session, tr_bool enabled )
1874{
1875    struct port_forwarding_data * d;
1876    d = tr_new0( struct port_forwarding_data, 1 );
1877    d->shared = session->shared;
1878    d->enabled = enabled;
1879    tr_runInEventThread( session, setPortForwardingEnabled, d );
1880}
1881
1882tr_bool
1883tr_sessionIsPortForwardingEnabled( const tr_session * session )
1884{
1885    assert( tr_isSession( session ) );
1886
1887    return tr_sharedTraversalIsEnabled( session->shared );
1888}
1889
1890/***
1891****
1892***/
1893
1894static int
1895tr_stringEndsWith( const char * str, const char * end )
1896{
1897    const size_t slen = strlen( str );
1898    const size_t elen = strlen( end );
1899
1900    return slen >= elen && !memcmp( &str[slen - elen], end, elen );
1901}
1902
1903static void
1904loadBlocklists( tr_session * session )
1905{
1906    int         binCount = 0;
1907    int         newCount = 0;
1908    struct stat sb;
1909    char      * dirname;
1910    DIR *       odir = NULL;
1911    tr_list *   list = NULL;
1912    const tr_bool   isEnabled = session->isBlocklistEnabled;
1913
1914    /* walk through the directory and find blocklists */
1915    dirname = tr_buildPath( session->configDir, "blocklists", NULL );
1916    if( !stat( dirname,
1917               &sb ) && S_ISDIR( sb.st_mode )
1918      && ( ( odir = opendir( dirname ) ) ) )
1919    {
1920        struct dirent *d;
1921        for( d = readdir( odir ); d; d = readdir( odir ) )
1922        {
1923            char * filename;
1924
1925            if( !d->d_name || d->d_name[0] == '.' ) /* skip dotfiles, ., and ..
1926                                                      */
1927                continue;
1928
1929            filename = tr_buildPath( dirname, d->d_name, NULL );
1930
1931            if( tr_stringEndsWith( filename, ".bin" ) )
1932            {
1933                /* if we don't already have this blocklist, add it */
1934                if( !tr_list_find( list, filename,
1935                                   (TrListCompareFunc)strcmp ) )
1936                {
1937                    tr_list_append( &list,
1938                                   _tr_blocklistNew( filename, isEnabled ) );
1939                    ++binCount;
1940                }
1941            }
1942            else
1943            {
1944                /* strip out the file suffix, if there is one, and add ".bin"
1945                  instead */
1946                tr_blocklist * b;
1947                const char *   dot = strrchr( d->d_name, '.' );
1948                const int      len = dot ? dot - d->d_name
1949                                         : (int)strlen( d->d_name );
1950                char         * tmp = tr_strdup_printf(
1951                                        "%s" TR_PATH_DELIMITER_STR "%*.*s.bin",
1952                                        dirname, len, len, d->d_name );
1953                b = _tr_blocklistNew( tmp, isEnabled );
1954                _tr_blocklistSetContent( b, filename );
1955                tr_list_append( &list, b );
1956                ++newCount;
1957                tr_free( tmp );
1958            }
1959
1960            tr_free( filename );
1961        }
1962
1963        closedir( odir );
1964    }
1965
1966    session->blocklists = list;
1967
1968    if( binCount )
1969        tr_dbg( "Found %d blocklists in \"%s\"", binCount, dirname );
1970    if( newCount )
1971        tr_dbg( "Found %d new blocklists in \"%s\"", newCount, dirname );
1972
1973    tr_free( dirname );
1974}
1975
1976static void
1977closeBlocklists( tr_session * session )
1978{
1979    tr_list_free( &session->blocklists,
1980                  (TrListForeachFunc)_tr_blocklistFree );
1981}
1982
1983void
1984tr_sessionReloadBlocklists( tr_session * session )
1985{
1986    closeBlocklists( session );
1987    loadBlocklists( session );
1988
1989    tr_peerMgrOnBlocklistChanged( session->peerMgr );
1990}
1991
1992int
1993tr_blocklistGetRuleCount( const tr_session * session )
1994{
1995    int       n = 0;
1996    tr_list * l;
1997
1998    assert( tr_isSession( session ) );
1999
2000    for( l = session->blocklists; l; l = l->next )
2001        n += _tr_blocklistGetRuleCount( l->data );
2002    return n;
2003}
2004
2005tr_bool
2006tr_blocklistIsEnabled( const tr_session * session )
2007{
2008    assert( tr_isSession( session ) );
2009
2010    return session->isBlocklistEnabled;
2011}
2012
2013void
2014tr_blocklistSetEnabled( tr_session * session,
2015                        tr_bool      isEnabled )
2016{
2017    tr_list * l;
2018
2019    assert( tr_isSession( session ) );
2020
2021    session->isBlocklistEnabled = isEnabled != 0;
2022
2023    for( l=session->blocklists; l!=NULL; l=l->next )
2024        _tr_blocklistSetEnabled( l->data, isEnabled );
2025}
2026
2027tr_bool
2028tr_blocklistExists( const tr_session * session )
2029{
2030    assert( tr_isSession( session ) );
2031
2032    return session->blocklists != NULL;
2033}
2034
2035int
2036tr_blocklistSetContent( tr_session * session,
2037                        const char * contentFilename )
2038{
2039    tr_list * l;
2040    int ruleCount;
2041    tr_blocklist * b;
2042    const char * defaultName = "level1.bin";
2043    tr_sessionLock( session );
2044
2045    for( b = NULL, l = session->blocklists; !b && l; l = l->next )
2046        if( tr_stringEndsWith( _tr_blocklistGetFilename( l->data ),
2047                               defaultName ) )
2048            b = l->data;
2049
2050    if( !b )
2051    {
2052        char * path = tr_buildPath( session->configDir, "blocklists", defaultName, NULL );
2053        b = _tr_blocklistNew( path, session->isBlocklistEnabled );
2054        tr_list_append( &session->blocklists, b );
2055        tr_free( path );
2056    }
2057
2058    ruleCount = _tr_blocklistSetContent( b, contentFilename );
2059    tr_sessionUnlock( session );
2060    return ruleCount;
2061}
2062
2063tr_bool
2064tr_sessionIsAddressBlocked( const tr_session * session,
2065                            const tr_address * addr )
2066{
2067    tr_list * l;
2068
2069    assert( tr_isSession( session ) );
2070
2071    for( l = session->blocklists; l; l = l->next )
2072        if( _tr_blocklistHasAddress( l->data, addr ) )
2073            return TRUE;
2074    return FALSE;
2075}
2076
2077/***
2078****
2079***/
2080
2081static void
2082metainfoLookupInit( tr_session * session )
2083{
2084    struct stat  sb;
2085    const char * dirname = tr_getTorrentDir( session );
2086    DIR *        odir = NULL;
2087    tr_ctor *    ctor = NULL;
2088    tr_benc * lookup;
2089    int n = 0;
2090
2091    assert( tr_isSession( session ) );
2092
2093    /* walk through the directory and find the mappings */
2094    lookup = tr_new0( tr_benc, 1 );
2095    tr_bencInitDict( lookup, 0 );
2096    ctor = tr_ctorNew( session );
2097    tr_ctorSetSave( ctor, FALSE ); /* since we already have them */
2098    if( !stat( dirname, &sb ) && S_ISDIR( sb.st_mode ) && ( ( odir = opendir( dirname ) ) ) )
2099    {
2100        struct dirent *d;
2101        while(( d = readdir( odir )))
2102        {
2103            if( tr_str_has_suffix( d->d_name, ".torrent" ) )
2104            {
2105                tr_info inf;
2106                char * path = tr_buildPath( dirname, d->d_name, NULL );
2107                tr_ctorSetMetainfoFromFile( ctor, path );
2108                if( !tr_torrentParse( ctor, &inf ) )
2109                {
2110                    ++n;
2111                    tr_bencDictAddStr( lookup, inf.hashString, path );
2112                }
2113                tr_free( path );
2114            }
2115        }
2116        closedir( odir );
2117    }
2118    tr_ctorFree( ctor );
2119
2120    session->metainfoLookup = lookup;
2121    tr_dbg( "Found %d torrents in \"%s\"", n, dirname );
2122}
2123
2124const char*
2125tr_sessionFindTorrentFile( const tr_session * session,
2126                           const char       * hashString )
2127{
2128    const char * filename = NULL;
2129    if( !session->metainfoLookup )
2130        metainfoLookupInit( (tr_session*)session );
2131    tr_bencDictFindStr( session->metainfoLookup, hashString, &filename );
2132    return filename;
2133}
2134
2135void
2136tr_sessionSetTorrentFile( tr_session * session,
2137                          const char * hashString,
2138                          const char * filename )
2139{
2140    /* since we walk session->configDir/torrents/ to build the lookup table,
2141     * and tr_sessionSetTorrentFile() is just to tell us there's a new file
2142     * in that same directory, we don't need to do anything here if the
2143     * lookup table hasn't been built yet */
2144    if( session->metainfoLookup )
2145        tr_bencDictAddStr( session->metainfoLookup, hashString, filename );
2146}
2147
2148tr_torrent*
2149tr_torrentNext( tr_session * session,
2150                tr_torrent * tor )
2151{
2152    tr_torrent * ret;
2153
2154    assert( !session || tr_isSession( session ) );
2155
2156    if( !session )
2157        ret = NULL;
2158    else if( !tor )
2159        ret = session->torrentList;
2160    else
2161        ret = tor->next;
2162
2163    return ret;
2164}
2165
2166/***
2167****
2168***/
2169
2170void
2171tr_sessionSetRPCEnabled( tr_session * session,
2172                         tr_bool      isEnabled )
2173{
2174    assert( tr_isSession( session ) );
2175
2176    tr_rpcSetEnabled( session->rpcServer, isEnabled );
2177}
2178
2179tr_bool
2180tr_sessionIsRPCEnabled( const tr_session * session )
2181{
2182    assert( tr_isSession( session ) );
2183
2184    return tr_rpcIsEnabled( session->rpcServer );
2185}
2186
2187void
2188tr_sessionSetRPCPort( tr_session * session,
2189                      tr_port      port )
2190{
2191    assert( tr_isSession( session ) );
2192
2193    tr_rpcSetPort( session->rpcServer, port );
2194}
2195
2196tr_port
2197tr_sessionGetRPCPort( const tr_session * session )
2198{
2199    assert( tr_isSession( session ) );
2200
2201    return tr_rpcGetPort( session->rpcServer );
2202}
2203
2204void
2205tr_sessionSetRPCCallback( tr_session * session,
2206                          tr_rpc_func  func,
2207                          void *       user_data )
2208{
2209    assert( tr_isSession( session ) );
2210
2211    session->rpc_func = func;
2212    session->rpc_func_user_data = user_data;
2213}
2214
2215void
2216tr_sessionSetRPCWhitelist( tr_session * session,
2217                           const char * whitelist )
2218{
2219    assert( tr_isSession( session ) );
2220
2221    tr_rpcSetWhitelist( session->rpcServer, whitelist );
2222}
2223
2224const char*
2225tr_sessionGetRPCWhitelist( const tr_session * session )
2226{
2227    assert( tr_isSession( session ) );
2228
2229    return tr_rpcGetWhitelist( session->rpcServer );
2230}
2231
2232void
2233tr_sessionSetRPCWhitelistEnabled( tr_session * session,
2234                                  tr_bool      isEnabled )
2235{
2236    assert( tr_isSession( session ) );
2237
2238    tr_rpcSetWhitelistEnabled( session->rpcServer, isEnabled );
2239}
2240
2241tr_bool
2242tr_sessionGetRPCWhitelistEnabled( const tr_session * session )
2243{
2244    assert( tr_isSession( session ) );
2245
2246    return tr_rpcGetWhitelistEnabled( session->rpcServer );
2247}
2248
2249
2250void
2251tr_sessionSetRPCPassword( tr_session * session,
2252                          const char * password )
2253{
2254    assert( tr_isSession( session ) );
2255
2256    tr_rpcSetPassword( session->rpcServer, password );
2257}
2258
2259const char*
2260tr_sessionGetRPCPassword( const tr_session * session )
2261{
2262    assert( tr_isSession( session ) );
2263
2264    return tr_rpcGetPassword( session->rpcServer );
2265}
2266
2267void
2268tr_sessionSetRPCUsername( tr_session * session,
2269                          const char * username )
2270{
2271    assert( tr_isSession( session ) );
2272
2273    tr_rpcSetUsername( session->rpcServer, username );
2274}
2275
2276const char*
2277tr_sessionGetRPCUsername( const tr_session * session )
2278{
2279    assert( tr_isSession( session ) );
2280
2281    return tr_rpcGetUsername( session->rpcServer );
2282}
2283
2284void
2285tr_sessionSetRPCPasswordEnabled( tr_session * session,
2286                                 tr_bool      isEnabled )
2287{
2288    assert( tr_isSession( session ) );
2289
2290    tr_rpcSetPasswordEnabled( session->rpcServer, isEnabled );
2291}
2292
2293tr_bool
2294tr_sessionIsRPCPasswordEnabled( const tr_session * session )
2295{
2296    assert( tr_isSession( session ) );
2297
2298    return tr_rpcIsPasswordEnabled( session->rpcServer );
2299}
2300
2301const char *
2302tr_sessionGetRPCBindAddress( const tr_session * session )
2303{
2304    assert( tr_isSession( session ) );
2305
2306    return tr_rpcGetBindAddress( session->rpcServer );
2307}
2308
2309/***
2310****
2311***/
2312
2313tr_bool
2314tr_sessionIsProxyEnabled( const tr_session * session )
2315{
2316    assert( tr_isSession( session ) );
2317
2318    return session->isProxyEnabled;
2319}
2320
2321void
2322tr_sessionSetProxyEnabled( tr_session * session,
2323                           tr_bool      isEnabled )
2324{
2325    assert( tr_isSession( session ) );
2326    assert( tr_isBool( isEnabled ) );
2327
2328    session->isProxyEnabled = isEnabled != 0;
2329}
2330
2331tr_proxy_type
2332tr_sessionGetProxyType( const tr_session * session )
2333{
2334    assert( tr_isSession( session ) );
2335
2336    return session->proxyType;
2337}
2338
2339void
2340tr_sessionSetProxyType( tr_session *  session,
2341                        tr_proxy_type type )
2342{
2343    assert( tr_isSession( session ) );
2344
2345    session->proxyType = type;
2346}
2347
2348const char*
2349tr_sessionGetProxy( const tr_session * session )
2350{
2351    assert( tr_isSession( session ) );
2352
2353    return session->proxy;
2354}
2355
2356tr_port
2357tr_sessionGetProxyPort( const tr_session * session )
2358{
2359    assert( tr_isSession( session ) );
2360
2361    return session->proxyPort;
2362}
2363
2364void
2365tr_sessionSetProxy( tr_session * session,
2366                    const char * proxy )
2367{
2368    assert( tr_isSession( session ) );
2369
2370    if( proxy != session->proxy )
2371    {
2372        tr_free( session->proxy );
2373        session->proxy = tr_strdup( proxy );
2374    }
2375}
2376
2377void
2378tr_sessionSetProxyPort( tr_session * session,
2379                        tr_port      port )
2380{
2381    assert( tr_isSession( session ) );
2382
2383    session->proxyPort = port;
2384}
2385
2386tr_bool
2387tr_sessionIsProxyAuthEnabled( const tr_session * session )
2388{
2389    assert( tr_isSession( session ) );
2390
2391    return session->isProxyAuthEnabled;
2392}
2393
2394void
2395tr_sessionSetProxyAuthEnabled( tr_session * session,
2396                               tr_bool      isEnabled )
2397{
2398    assert( tr_isSession( session ) );
2399    assert( tr_isBool( isEnabled ) );
2400
2401    session->isProxyAuthEnabled = isEnabled != 0;
2402}
2403
2404const char*
2405tr_sessionGetProxyUsername( const tr_session * session )
2406{
2407    assert( tr_isSession( session ) );
2408
2409    return session->proxyUsername;
2410}
2411
2412void
2413tr_sessionSetProxyUsername( tr_session * session,
2414                            const char * username )
2415{
2416    assert( tr_isSession( session ) );
2417
2418    if( username != session->proxyUsername )
2419    {
2420        tr_free( session->proxyUsername );
2421        session->proxyUsername = tr_strdup( username );
2422    }
2423}
2424
2425const char*
2426tr_sessionGetProxyPassword( const tr_session * session )
2427{
2428    assert( tr_isSession( session ) );
2429
2430    return session->proxyPassword;
2431}
2432
2433void
2434tr_sessionSetProxyPassword( tr_session * session,
2435                            const char * password )
2436{
2437    assert( tr_isSession( session ) );
2438
2439    if( password != session->proxyPassword )
2440    {
2441        tr_free( session->proxyPassword );
2442        session->proxyPassword = tr_strdup( password );
2443    }
2444}
2445
2446/****
2447*****
2448****/
2449
2450tr_bool
2451tr_sessionIsTorrentDoneScriptEnabled( const tr_session * session )
2452{
2453    assert( tr_isSession( session ) );
2454
2455    return session->isTorrentDoneScriptEnabled;
2456}
2457
2458void
2459tr_sessionSetTorrentDoneScriptEnabled( tr_session * session, tr_bool isEnabled )
2460{
2461    assert( tr_isSession( session ) );
2462    assert( tr_isBool( isEnabled ) );
2463
2464    session->isTorrentDoneScriptEnabled = isEnabled;
2465}
2466
2467const char *
2468tr_sessionGetTorrentDoneScript( const tr_session * session )
2469{
2470    assert( tr_isSession( session ) );
2471
2472    return session->torrentDoneScript;
2473}
2474
2475void
2476tr_sessionSetTorrentDoneScript( tr_session * session, const char * scriptFilename )
2477{
2478    assert( tr_isSession( session ) );
2479
2480    if( session->torrentDoneScript != scriptFilename )
2481    {
2482        tr_free( session->torrentDoneScript );
2483        session->torrentDoneScript = tr_strdup( scriptFilename );
2484    }
2485}
Note: See TracBrowser for help on using the repository browser.