source: trunk/libtransmission/session.c @ 10550

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

(trunk) minor transmission.h API cleanup.

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