source: trunk/libtransmission/session.c @ 11629

Last change on this file since 11629 was 11629, checked in by jordan, 11 years ago

(trunk libT) prevent crash on shutdown when bindinfo ptr can be NULL

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