source: trunk/libtransmission/session.c @ 8244

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

(trunk libT) #1992: try to cleanly destroy the "verify local data" thread on shutdown

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