source: trunk/libtransmission/session.c @ 8745

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

(trunk libT) #2244: Default upload slots too high

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