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

Last change on this file since 11467 was 11467, checked in by charles, 11 years ago

(2.0x libtransmission) backport r11449 for #3787 "'No Announce Scheduled' tracker error"

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