source: trunk/libtransmission/session.c @ 10751

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

(trunk libT) as long as I'm committing these CPU tweaks, might as well throw in #3289 too. To undo all this, we can revert to r10745

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