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

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

(trunk 2.0x) #3493 "transmission doesn't exit cleanly if LPD is disabled during the session" -- fixed.

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