source: trunk/libtransmission/session.c @ 10937

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

(trunk) #3045 "speed units" -- change the public API of libtransmission based on feedback from livings

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