source: trunk/libtransmission/session.c @ 11524

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

(trunk) #1538 "Make Web UI URL configurable" -- added to trunk. Patch by wereHamster

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