source: trunk/libtransmission/session.c @ 10638

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

(trunk) #1796 "run script after torrent completion" -- implemented for libT, RPC, and the GTK+ and Qt clients

  • 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 10638 2010-05-08 22:42:28Z 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( d->d_name && d->d_name[0] != '.' ) /* skip dotfiles, ., and ..
1724                                                     */
1725            {
1726                tr_torrent * tor;
1727                char * path = tr_buildPath( dirname, d->d_name, NULL );
1728                tr_ctorSetMetainfoFromFile( ctor, path );
1729                if(( tor = tr_torrentNew( ctor, NULL )))
1730                {
1731                    tr_list_append( &list, tor );
1732                    ++n;
1733                }
1734                tr_free( path );
1735            }
1736        }
1737        closedir( odir );
1738    }
1739
1740    torrents = tr_new( tr_torrent *, n );
1741    for( i = 0, l = list; l != NULL; l = l->next )
1742        torrents[i++] = (tr_torrent*) l->data;
1743    assert( i == n );
1744
1745    tr_list_free( &list, NULL );
1746
1747    if( n )
1748        tr_inf( _( "Loaded %d torrents" ), n );
1749
1750    if( setmeCount )
1751        *setmeCount = n;
1752    return torrents;
1753}
1754
1755/***
1756****
1757***/
1758
1759void
1760tr_sessionSetPexEnabled( tr_session * session,
1761                         tr_bool      enabled )
1762{
1763    assert( tr_isSession( session ) );
1764
1765    session->isPexEnabled = enabled != 0;
1766}
1767
1768tr_bool
1769tr_sessionIsPexEnabled( const tr_session * session )
1770{
1771    assert( tr_isSession( session ) );
1772
1773    return session->isPexEnabled;
1774}
1775
1776tr_bool
1777tr_sessionAllowsDHT( const tr_session * session UNUSED )
1778{
1779    return tr_sessionIsDHTEnabled( session );
1780}
1781
1782tr_bool
1783tr_sessionIsDHTEnabled( const tr_session * session )
1784{
1785    assert( tr_isSession( session ) );
1786
1787    return session->isDHTEnabled;
1788}
1789
1790static void
1791toggleDHTImpl(  void * data )
1792{
1793    tr_session * session = data;
1794    assert( tr_isSession( session ) );
1795
1796    if( session->isDHTEnabled )
1797        tr_dhtUninit( session );
1798
1799    session->isDHTEnabled = !session->isDHTEnabled;
1800
1801    if( session->isDHTEnabled )
1802        tr_dhtInit( session, &session->public_ipv4->addr );
1803}
1804
1805void
1806tr_sessionSetDHTEnabled( tr_session * session, tr_bool enabled )
1807{
1808    assert( tr_isSession( session ) );
1809    assert( tr_isBool( enabled ) );
1810
1811    if( ( enabled != 0 ) != ( session->isDHTEnabled != 0 ) )
1812        tr_runInEventThread( session, toggleDHTImpl, session );
1813}
1814
1815void
1816tr_sessionSetLPDEnabled( tr_session * session,
1817                         tr_bool      enabled )
1818{
1819    assert( tr_isSession( session ) );
1820
1821    session->isLPDEnabled = ( enabled != 0 );
1822}
1823
1824tr_bool
1825tr_sessionIsLPDEnabled( const tr_session * session )
1826{
1827    assert( tr_isSession( session ) );
1828
1829    return session->isLPDEnabled;
1830}
1831
1832tr_bool
1833tr_sessionAllowsLPD( const tr_session * session )
1834{
1835    return tr_sessionIsLPDEnabled( session );
1836}
1837
1838/***
1839****
1840***/
1841
1842void
1843tr_sessionSetLazyBitfieldEnabled( tr_session * session,
1844                                  tr_bool      enabled )
1845{
1846    assert( tr_isSession( session ) );
1847
1848    session->useLazyBitfield = enabled != 0;
1849}
1850
1851tr_bool
1852tr_sessionIsLazyBitfieldEnabled( const tr_session * session )
1853{
1854    assert( tr_isSession( session ) );
1855
1856    return session->useLazyBitfield;
1857}
1858
1859/***
1860****
1861***/
1862
1863struct port_forwarding_data
1864{
1865    tr_bool enabled;
1866    struct tr_shared * shared;
1867};
1868
1869static void
1870setPortForwardingEnabled( void * vdata )
1871{
1872    struct port_forwarding_data * data = vdata;
1873    tr_sharedTraversalEnable( data->shared, data->enabled );
1874    tr_free( data );
1875}
1876
1877void
1878tr_sessionSetPortForwardingEnabled( tr_session  * session, tr_bool enabled )
1879{
1880    struct port_forwarding_data * d;
1881    d = tr_new0( struct port_forwarding_data, 1 );
1882    d->shared = session->shared;
1883    d->enabled = enabled;
1884    tr_runInEventThread( session, setPortForwardingEnabled, d );
1885}
1886
1887tr_bool
1888tr_sessionIsPortForwardingEnabled( const tr_session * session )
1889{
1890    assert( tr_isSession( session ) );
1891
1892    return tr_sharedTraversalIsEnabled( session->shared );
1893}
1894
1895/***
1896****
1897***/
1898
1899static int
1900tr_stringEndsWith( const char * str, const char * end )
1901{
1902    const size_t slen = strlen( str );
1903    const size_t elen = strlen( end );
1904
1905    return slen >= elen && !memcmp( &str[slen - elen], end, elen );
1906}
1907
1908static void
1909loadBlocklists( tr_session * session )
1910{
1911    int         binCount = 0;
1912    int         newCount = 0;
1913    struct stat sb;
1914    char      * dirname;
1915    DIR *       odir = NULL;
1916    tr_list *   list = NULL;
1917    const tr_bool   isEnabled = session->isBlocklistEnabled;
1918
1919    /* walk through the directory and find blocklists */
1920    dirname = tr_buildPath( session->configDir, "blocklists", NULL );
1921    if( !stat( dirname,
1922               &sb ) && S_ISDIR( sb.st_mode )
1923      && ( ( odir = opendir( dirname ) ) ) )
1924    {
1925        struct dirent *d;
1926        for( d = readdir( odir ); d; d = readdir( odir ) )
1927        {
1928            char * filename;
1929
1930            if( !d->d_name || d->d_name[0] == '.' ) /* skip dotfiles, ., and ..
1931                                                      */
1932                continue;
1933
1934            filename = tr_buildPath( dirname, d->d_name, NULL );
1935
1936            if( tr_stringEndsWith( filename, ".bin" ) )
1937            {
1938                /* if we don't already have this blocklist, add it */
1939                if( !tr_list_find( list, filename,
1940                                   (TrListCompareFunc)strcmp ) )
1941                {
1942                    tr_list_append( &list,
1943                                   _tr_blocklistNew( filename, isEnabled ) );
1944                    ++binCount;
1945                }
1946            }
1947            else
1948            {
1949                /* strip out the file suffix, if there is one, and add ".bin"
1950                  instead */
1951                tr_blocklist * b;
1952                const char *   dot = strrchr( d->d_name, '.' );
1953                const int      len = dot ? dot - d->d_name
1954                                         : (int)strlen( d->d_name );
1955                char         * tmp = tr_strdup_printf(
1956                                        "%s" TR_PATH_DELIMITER_STR "%*.*s.bin",
1957                                        dirname, len, len, d->d_name );
1958                b = _tr_blocklistNew( tmp, isEnabled );
1959                _tr_blocklistSetContent( b, filename );
1960                tr_list_append( &list, b );
1961                ++newCount;
1962                tr_free( tmp );
1963            }
1964
1965            tr_free( filename );
1966        }
1967
1968        closedir( odir );
1969    }
1970
1971    session->blocklists = list;
1972
1973    if( binCount )
1974        tr_dbg( "Found %d blocklists in \"%s\"", binCount, dirname );
1975    if( newCount )
1976        tr_dbg( "Found %d new blocklists in \"%s\"", newCount, dirname );
1977
1978    tr_free( dirname );
1979}
1980
1981static void
1982closeBlocklists( tr_session * session )
1983{
1984    tr_list_free( &session->blocklists,
1985                  (TrListForeachFunc)_tr_blocklistFree );
1986}
1987
1988void
1989tr_sessionReloadBlocklists( tr_session * session )
1990{
1991    closeBlocklists( session );
1992    loadBlocklists( session );
1993}
1994
1995int
1996tr_blocklistGetRuleCount( const tr_session * session )
1997{
1998    int       n = 0;
1999    tr_list * l;
2000
2001    assert( tr_isSession( session ) );
2002
2003    for( l = session->blocklists; l; l = l->next )
2004        n += _tr_blocklistGetRuleCount( l->data );
2005    return n;
2006}
2007
2008tr_bool
2009tr_blocklistIsEnabled( const tr_session * session )
2010{
2011    assert( tr_isSession( session ) );
2012
2013    return session->isBlocklistEnabled;
2014}
2015
2016void
2017tr_blocklistSetEnabled( tr_session * session,
2018                        tr_bool      isEnabled )
2019{
2020    tr_list * l;
2021
2022    assert( tr_isSession( session ) );
2023
2024    session->isBlocklistEnabled = isEnabled != 0;
2025
2026    for( l=session->blocklists; l!=NULL; l=l->next )
2027        _tr_blocklistSetEnabled( l->data, isEnabled );
2028}
2029
2030tr_bool
2031tr_blocklistExists( const tr_session * session )
2032{
2033    assert( tr_isSession( session ) );
2034
2035    return session->blocklists != NULL;
2036}
2037
2038int
2039tr_blocklistSetContent( tr_session * session,
2040                        const char * contentFilename )
2041{
2042    tr_list * l;
2043    int ruleCount;
2044    tr_blocklist * b;
2045    const char * defaultName = "level1.bin";
2046    tr_sessionLock( session );
2047
2048    for( b = NULL, l = session->blocklists; !b && l; l = l->next )
2049        if( tr_stringEndsWith( _tr_blocklistGetFilename( l->data ),
2050                               defaultName ) )
2051            b = l->data;
2052
2053    if( !b )
2054    {
2055        char * path = tr_buildPath( session->configDir, "blocklists", defaultName, NULL );
2056        b = _tr_blocklistNew( path, session->isBlocklistEnabled );
2057        tr_list_append( &session->blocklists, b );
2058        tr_free( path );
2059    }
2060
2061    ruleCount = _tr_blocklistSetContent( b, contentFilename );
2062    tr_sessionUnlock( session );
2063    return ruleCount;
2064}
2065
2066tr_bool
2067tr_sessionIsAddressBlocked( const tr_session * session,
2068                            const tr_address * addr )
2069{
2070    tr_list * l;
2071
2072    assert( tr_isSession( session ) );
2073
2074    for( l = session->blocklists; l; l = l->next )
2075        if( _tr_blocklistHasAddress( l->data, addr ) )
2076            return TRUE;
2077    return FALSE;
2078}
2079
2080/***
2081****
2082***/
2083
2084static void
2085metainfoLookupInit( tr_session * session )
2086{
2087    struct stat  sb;
2088    const char * dirname = tr_getTorrentDir( session );
2089    DIR *        odir = NULL;
2090    tr_ctor *    ctor = NULL;
2091    tr_benc * lookup;
2092    int n = 0;
2093
2094    assert( tr_isSession( session ) );
2095
2096    /* walk through the directory and find the mappings */
2097    lookup = tr_new0( tr_benc, 1 );
2098    tr_bencInitDict( lookup, 0 );
2099    ctor = tr_ctorNew( session );
2100    tr_ctorSetSave( ctor, FALSE ); /* since we already have them */
2101    if( !stat( dirname, &sb ) && S_ISDIR( sb.st_mode ) && ( ( odir = opendir( dirname ) ) ) )
2102    {
2103        struct dirent *d;
2104        while(( d = readdir( odir )))
2105        {
2106            if( d->d_name && d->d_name[0] != '.' )
2107            {
2108                tr_info inf;
2109                char * path = tr_buildPath( dirname, d->d_name, NULL );
2110                tr_ctorSetMetainfoFromFile( ctor, path );
2111                if( !tr_torrentParse( ctor, &inf ) )
2112                {
2113                    ++n;
2114                    tr_bencDictAddStr( lookup, inf.hashString, path );
2115                }
2116                tr_free( path );
2117            }
2118        }
2119        closedir( odir );
2120    }
2121    tr_ctorFree( ctor );
2122
2123    session->metainfoLookup = lookup;
2124    tr_dbg( "Found %d torrents in \"%s\"", n, dirname );
2125}
2126
2127const char*
2128tr_sessionFindTorrentFile( const tr_session * session,
2129                           const char       * hashString )
2130{
2131    const char * filename = NULL;
2132    if( !session->metainfoLookup )
2133        metainfoLookupInit( (tr_session*)session );
2134    tr_bencDictFindStr( session->metainfoLookup, hashString, &filename );
2135    return filename;
2136}
2137
2138void
2139tr_sessionSetTorrentFile( tr_session * session,
2140                          const char * hashString,
2141                          const char * filename )
2142{
2143    /* since we walk session->configDir/torrents/ to build the lookup table,
2144     * and tr_sessionSetTorrentFile() is just to tell us there's a new file
2145     * in that same directory, we don't need to do anything here if the
2146     * lookup table hasn't been built yet */
2147    if( session->metainfoLookup )
2148        tr_bencDictAddStr( session->metainfoLookup, hashString, filename );
2149}
2150
2151tr_torrent*
2152tr_torrentNext( tr_session * session,
2153                tr_torrent * tor )
2154{
2155    tr_torrent * ret;
2156
2157    assert( !session || tr_isSession( session ) );
2158
2159    if( !session )
2160        ret = NULL;
2161    else if( !tor )
2162        ret = session->torrentList;
2163    else
2164        ret = tor->next;
2165
2166    return ret;
2167}
2168
2169/***
2170****
2171***/
2172
2173void
2174tr_sessionSetRPCEnabled( tr_session * session,
2175                         tr_bool      isEnabled )
2176{
2177    assert( tr_isSession( session ) );
2178
2179    tr_rpcSetEnabled( session->rpcServer, isEnabled );
2180}
2181
2182tr_bool
2183tr_sessionIsRPCEnabled( const tr_session * session )
2184{
2185    assert( tr_isSession( session ) );
2186
2187    return tr_rpcIsEnabled( session->rpcServer );
2188}
2189
2190void
2191tr_sessionSetRPCPort( tr_session * session,
2192                      tr_port      port )
2193{
2194    assert( tr_isSession( session ) );
2195
2196    tr_rpcSetPort( session->rpcServer, port );
2197}
2198
2199tr_port
2200tr_sessionGetRPCPort( const tr_session * session )
2201{
2202    assert( tr_isSession( session ) );
2203
2204    return tr_rpcGetPort( session->rpcServer );
2205}
2206
2207void
2208tr_sessionSetRPCCallback( tr_session * session,
2209                          tr_rpc_func  func,
2210                          void *       user_data )
2211{
2212    assert( tr_isSession( session ) );
2213
2214    session->rpc_func = func;
2215    session->rpc_func_user_data = user_data;
2216}
2217
2218void
2219tr_sessionSetRPCWhitelist( tr_session * session,
2220                           const char * whitelist )
2221{
2222    assert( tr_isSession( session ) );
2223
2224    tr_rpcSetWhitelist( session->rpcServer, whitelist );
2225}
2226
2227const char*
2228tr_sessionGetRPCWhitelist( const tr_session * session )
2229{
2230    assert( tr_isSession( session ) );
2231
2232    return tr_rpcGetWhitelist( session->rpcServer );
2233}
2234
2235void
2236tr_sessionSetRPCWhitelistEnabled( tr_session * session,
2237                                  tr_bool      isEnabled )
2238{
2239    assert( tr_isSession( session ) );
2240
2241    tr_rpcSetWhitelistEnabled( session->rpcServer, isEnabled );
2242}
2243
2244tr_bool
2245tr_sessionGetRPCWhitelistEnabled( const tr_session * session )
2246{
2247    assert( tr_isSession( session ) );
2248
2249    return tr_rpcGetWhitelistEnabled( session->rpcServer );
2250}
2251
2252
2253void
2254tr_sessionSetRPCPassword( tr_session * session,
2255                          const char * password )
2256{
2257    assert( tr_isSession( session ) );
2258
2259    tr_rpcSetPassword( session->rpcServer, password );
2260}
2261
2262const char*
2263tr_sessionGetRPCPassword( const tr_session * session )
2264{
2265    assert( tr_isSession( session ) );
2266
2267    return tr_rpcGetPassword( session->rpcServer );
2268}
2269
2270void
2271tr_sessionSetRPCUsername( tr_session * session,
2272                          const char * username )
2273{
2274    assert( tr_isSession( session ) );
2275
2276    tr_rpcSetUsername( session->rpcServer, username );
2277}
2278
2279const char*
2280tr_sessionGetRPCUsername( const tr_session * session )
2281{
2282    assert( tr_isSession( session ) );
2283
2284    return tr_rpcGetUsername( session->rpcServer );
2285}
2286
2287void
2288tr_sessionSetRPCPasswordEnabled( tr_session * session,
2289                                 tr_bool      isEnabled )
2290{
2291    assert( tr_isSession( session ) );
2292
2293    tr_rpcSetPasswordEnabled( session->rpcServer, isEnabled );
2294}
2295
2296tr_bool
2297tr_sessionIsRPCPasswordEnabled( const tr_session * session )
2298{
2299    assert( tr_isSession( session ) );
2300
2301    return tr_rpcIsPasswordEnabled( session->rpcServer );
2302}
2303
2304const char *
2305tr_sessionGetRPCBindAddress( const tr_session * session )
2306{
2307    assert( tr_isSession( session ) );
2308
2309    return tr_rpcGetBindAddress( session->rpcServer );
2310}
2311
2312/***
2313****
2314***/
2315
2316tr_bool
2317tr_sessionIsProxyEnabled( const tr_session * session )
2318{
2319    assert( tr_isSession( session ) );
2320
2321    return session->isProxyEnabled;
2322}
2323
2324void
2325tr_sessionSetProxyEnabled( tr_session * session,
2326                           tr_bool      isEnabled )
2327{
2328    assert( tr_isSession( session ) );
2329    assert( tr_isBool( isEnabled ) );
2330
2331    session->isProxyEnabled = isEnabled != 0;
2332}
2333
2334tr_proxy_type
2335tr_sessionGetProxyType( const tr_session * session )
2336{
2337    assert( tr_isSession( session ) );
2338
2339    return session->proxyType;
2340}
2341
2342void
2343tr_sessionSetProxyType( tr_session *  session,
2344                        tr_proxy_type type )
2345{
2346    assert( tr_isSession( session ) );
2347
2348    session->proxyType = type;
2349}
2350
2351const char*
2352tr_sessionGetProxy( const tr_session * session )
2353{
2354    assert( tr_isSession( session ) );
2355
2356    return session->proxy;
2357}
2358
2359tr_port
2360tr_sessionGetProxyPort( const tr_session * session )
2361{
2362    assert( tr_isSession( session ) );
2363
2364    return session->proxyPort;
2365}
2366
2367void
2368tr_sessionSetProxy( tr_session * session,
2369                    const char * proxy )
2370{
2371    assert( tr_isSession( session ) );
2372
2373    if( proxy != session->proxy )
2374    {
2375        tr_free( session->proxy );
2376        session->proxy = tr_strdup( proxy );
2377    }
2378}
2379
2380void
2381tr_sessionSetProxyPort( tr_session * session,
2382                        tr_port      port )
2383{
2384    assert( tr_isSession( session ) );
2385
2386    session->proxyPort = port;
2387}
2388
2389tr_bool
2390tr_sessionIsProxyAuthEnabled( const tr_session * session )
2391{
2392    assert( tr_isSession( session ) );
2393
2394    return session->isProxyAuthEnabled;
2395}
2396
2397void
2398tr_sessionSetProxyAuthEnabled( tr_session * session,
2399                               tr_bool      isEnabled )
2400{
2401    assert( tr_isSession( session ) );
2402    assert( tr_isBool( isEnabled ) );
2403
2404    session->isProxyAuthEnabled = isEnabled != 0;
2405}
2406
2407const char*
2408tr_sessionGetProxyUsername( const tr_session * session )
2409{
2410    assert( tr_isSession( session ) );
2411
2412    return session->proxyUsername;
2413}
2414
2415void
2416tr_sessionSetProxyUsername( tr_session * session,
2417                            const char * username )
2418{
2419    assert( tr_isSession( session ) );
2420
2421    if( username != session->proxyUsername )
2422    {
2423        tr_free( session->proxyUsername );
2424        session->proxyUsername = tr_strdup( username );
2425    }
2426}
2427
2428const char*
2429tr_sessionGetProxyPassword( const tr_session * session )
2430{
2431    assert( tr_isSession( session ) );
2432
2433    return session->proxyPassword;
2434}
2435
2436void
2437tr_sessionSetProxyPassword( tr_session * session,
2438                            const char * password )
2439{
2440    assert( tr_isSession( session ) );
2441
2442    if( password != session->proxyPassword )
2443    {
2444        tr_free( session->proxyPassword );
2445        session->proxyPassword = tr_strdup( password );
2446    }
2447}
2448
2449/****
2450*****
2451****/
2452
2453tr_bool
2454tr_sessionIsTorrentDoneScriptEnabled( const tr_session * session )
2455{
2456    assert( tr_isSession( session ) );
2457
2458    return session->isTorrentDoneScriptEnabled;
2459}
2460
2461void
2462tr_sessionSetTorrentDoneScriptEnabled( tr_session * session, tr_bool isEnabled )
2463{
2464    assert( tr_isSession( session ) );
2465    assert( tr_isBool( isEnabled ) );
2466
2467    session->isTorrentDoneScriptEnabled = isEnabled;
2468}
2469
2470const char *
2471tr_sessionGetTorrentDoneScript( const tr_session * session )
2472{
2473    assert( tr_isSession( session ) );
2474
2475    return session->torrentDoneScript;
2476}
2477
2478void
2479tr_sessionSetTorrentDoneScript( tr_session * session, const char * scriptFilename )
2480{
2481    assert( tr_isSession( session ) );
2482
2483    if( session->torrentDoneScript != scriptFilename )
2484    {
2485        tr_free( session->torrentDoneScript );
2486        session->torrentDoneScript = tr_strdup( scriptFilename );
2487    }
2488}
Note: See TracBrowser for help on using the repository browser.