source: trunk/libtransmission/session.c @ 10931

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

(trunk) #3045 "make libtransmission's API byte-oriented instead of KiB-oriented." -- implemented. This is a largish commit and will break the mac build for a little while.

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