source: trunk/libtransmission/session.c @ 8889

Last change on this file since 8889 was 8889, checked in by charles, 13 years ago

(trunk) remove trailing spaces

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