source: trunk/libtransmission/session.c @ 7643

Last change on this file since 7643 was 7643, checked in by charles, 12 years ago

(trunk libT) make the default number of open files 32 rather than 16, and make it configurable via settings.json. (Reported by Lucius Windschuh via denis_)

  • Property svn:keywords set to Date Rev Author Id
File size: 40.1 KB
Line 
1/*
2 * This file Copyright (C) 2008 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 7643 2009-01-08 19:20:24Z 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 "transmission.h"
24#include "session.h"
25#include "bandwidth.h"
26#include "bencode.h"
27#include "blocklist.h"
28#include "fdlimit.h"
29#include "list.h"
30#include "metainfo.h" /* tr_metainfoFree */
31#include "net.h"
32#include "peer-mgr.h"
33#include "platform.h" /* tr_lock */
34#include "port-forwarding.h"
35#include "rpc-server.h"
36#include "stats.h"
37#include "torrent.h"
38#include "tracker.h"
39#include "trevent.h"
40#include "utils.h"
41#include "web.h"
42#include "crypto.h"
43
44#define dbgmsg( ... ) \
45    do { \
46        if( tr_deepLoggingIsActive( ) ) \
47            tr_deepLog( __FILE__, __LINE__, NULL, __VA_ARGS__ ); \
48    } while( 0 )
49
50static tr_port
51getRandomPort( tr_session * s )
52{
53    return tr_cryptoWeakRandInt( s->randomPortHigh - s->randomPortLow + 1) + s->randomPortLow;
54}
55
56/* Generate a peer id : "-TRxyzb-" + 12 random alphanumeric
57   characters, where x is the major version number, y is the
58   minor version number, z is the maintenance number, and b
59   designates beta (Azureus-style) */
60uint8_t*
61tr_peerIdNew( void )
62{
63    int          i;
64    int          val;
65    int          total = 0;
66    uint8_t *    buf = tr_new( uint8_t, 21 );
67    const char * pool = "0123456789abcdefghijklmnopqrstuvwxyz";
68    const int    base = 36;
69
70    memcpy( buf, PEERID_PREFIX, 8 );
71
72    for( i = 8; i < 19; ++i )
73    {
74        val = tr_cryptoRandInt( base );
75        total += val;
76        buf[i] = pool[val];
77    }
78
79    val = total % base ? base - ( total % base ) : 0;
80    buf[19] = pool[val];
81    buf[20] = '\0';
82
83    return buf;
84}
85
86const uint8_t*
87tr_getPeerId( void )
88{
89    static uint8_t * id = NULL;
90
91    if( id == NULL )
92        id = tr_peerIdNew( );
93    return id;
94}
95
96/***
97****
98***/
99
100tr_encryption_mode
101tr_sessionGetEncryption( tr_session * session )
102{
103    assert( session );
104
105    return session->encryptionMode;
106}
107
108void
109tr_sessionSetEncryption( tr_session *       session,
110                         tr_encryption_mode mode )
111{
112    assert( session );
113    assert( mode == TR_ENCRYPTION_PREFERRED
114          || mode == TR_ENCRYPTION_REQUIRED
115          || mode == TR_CLEAR_PREFERRED );
116
117    session->encryptionMode = mode;
118}
119
120/***
121****
122***/
123
124static int
125tr_stringEndsWith( const char * str,
126                   const char * end )
127{
128    const size_t slen = strlen( str );
129    const size_t elen = strlen( end );
130
131    return slen >= elen && !memcmp( &str[slen - elen], end, elen );
132}
133
134static void
135loadBlocklists( tr_session * session )
136{
137    int         binCount = 0;
138    int         newCount = 0;
139    struct stat sb;
140    char      * dirname;
141    DIR *       odir = NULL;
142    tr_list *   list = NULL;
143    const tr_bool   isEnabled = session->isBlocklistEnabled;
144
145    /* walk through the directory and find blocklists */
146    dirname = tr_buildPath( session->configDir, "blocklists", NULL );
147    if( !stat( dirname,
148               &sb ) && S_ISDIR( sb.st_mode )
149      && ( ( odir = opendir( dirname ) ) ) )
150    {
151        struct dirent *d;
152        for( d = readdir( odir ); d; d = readdir( odir ) )
153        {
154            char * filename;
155
156            if( !d->d_name || d->d_name[0] == '.' ) /* skip dotfiles, ., and ..
157                                                      */
158                continue;
159
160            filename = tr_buildPath( dirname, d->d_name, NULL );
161
162            if( tr_stringEndsWith( filename, ".bin" ) )
163            {
164                /* if we don't already have this blocklist, add it */
165                if( !tr_list_find( list, filename,
166                                   (TrListCompareFunc)strcmp ) )
167                {
168                    tr_list_append( &list,
169                                   _tr_blocklistNew( filename, isEnabled ) );
170                    ++binCount;
171                }
172            }
173            else
174            {
175                /* strip out the file suffix, if there is one, and add ".bin"
176                  instead */
177                tr_blocklist * b;
178                const char *   dot = strrchr( d->d_name, '.' );
179                const int      len = dot ? dot - d->d_name
180                                         : (int)strlen( d->d_name );
181                char         * tmp = tr_strdup_printf(
182                                        "%s" TR_PATH_DELIMITER_STR "%*.*s.bin",
183                                        dirname, len, len, d->d_name );
184                b = _tr_blocklistNew( tmp, isEnabled );
185                _tr_blocklistSetContent( b, filename );
186                tr_list_append( &list, b );
187                ++newCount;
188                tr_free( tmp );
189            }
190
191            tr_free( filename );
192        }
193
194        closedir( odir );
195    }
196
197    session->blocklists = list;
198
199    if( binCount )
200        tr_dbg( "Found %d blocklists in \"%s\"", binCount, dirname );
201    if( newCount )
202        tr_dbg( "Found %d new blocklists in \"%s\"", newCount, dirname );
203
204    tr_free( dirname );
205}
206
207/***
208****
209***/
210
211#ifdef TR_EMBEDDED
212 #define TR_DEFAULT_ENCRYPTION              TR_CLEAR_PREFERRED
213#else
214 #define TR_DEFAULT_ENCRYPTION              TR_ENCRYPTION_PREFERRED
215#endif
216
217void
218tr_sessionGetDefaultSettings( tr_benc * d )
219{
220    assert( tr_bencIsDict( d ) );
221
222    tr_bencDictReserve( d, 30 );
223    tr_bencDictAddInt( d, TR_PREFS_KEY_BLOCKLIST_ENABLED,        FALSE );
224    tr_bencDictAddStr( d, TR_PREFS_KEY_DOWNLOAD_DIR,             tr_getDefaultDownloadDir( ) );
225    tr_bencDictAddInt( d, TR_PREFS_KEY_DSPEED,                   100 );
226    tr_bencDictAddInt( d, TR_PREFS_KEY_DSPEED_ENABLED,           0 );
227    tr_bencDictAddInt( d, TR_PREFS_KEY_ENCRYPTION,               TR_DEFAULT_ENCRYPTION );
228    tr_bencDictAddInt( d, TR_PREFS_KEY_LAZY_BITFIELD,            TRUE );
229    tr_bencDictAddInt( d, TR_PREFS_KEY_MSGLEVEL,                 TR_MSG_INF );
230    tr_bencDictAddInt( d, TR_PREFS_KEY_OPEN_FILE_LIMIT,          atoi( TR_DEFAULT_OPEN_FILE_LIMIT_STR ) );
231    tr_bencDictAddInt( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL,        atoi( TR_DEFAULT_PEER_LIMIT_GLOBAL_STR ) );
232    tr_bencDictAddInt( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT,       atoi( TR_DEFAULT_PEER_LIMIT_TORRENT_STR ) );
233    tr_bencDictAddInt( d, TR_PREFS_KEY_PEER_PORT,                atoi( TR_DEFAULT_PEER_PORT_STR ) );
234    tr_bencDictAddInt( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ENABLED, FALSE );
235    tr_bencDictAddInt( d, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW,     1024 );
236    tr_bencDictAddInt( d, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH,    65535 );
237    tr_bencDictAddInt( d, TR_PREFS_KEY_PEER_SOCKET_TOS,          atoi( TR_DEFAULT_PEER_SOCKET_TOS_STR ) );
238    tr_bencDictAddInt( d, TR_PREFS_KEY_PEX_ENABLED,              TRUE );
239    tr_bencDictAddInt( d, TR_PREFS_KEY_PORT_FORWARDING,          TRUE );
240    tr_bencDictAddStr( d, TR_PREFS_KEY_PROXY,                    "" );
241    tr_bencDictAddInt( d, TR_PREFS_KEY_PROXY_AUTH_ENABLED,       FALSE );
242    tr_bencDictAddInt( d, TR_PREFS_KEY_PROXY_ENABLED,            FALSE );
243    tr_bencDictAddStr( d, TR_PREFS_KEY_PROXY_PASSWORD,           "" );
244    tr_bencDictAddInt( d, TR_PREFS_KEY_PROXY_PORT,               80 );
245    tr_bencDictAddInt( d, TR_PREFS_KEY_PROXY_TYPE,               TR_PROXY_HTTP );
246    tr_bencDictAddStr( d, TR_PREFS_KEY_PROXY_USERNAME,           "" );
247    tr_bencDictAddInt( d, TR_PREFS_KEY_RPC_AUTH_REQUIRED,        FALSE );
248    tr_bencDictAddInt( d, TR_PREFS_KEY_RPC_ENABLED,              TRUE );
249    tr_bencDictAddStr( d, TR_PREFS_KEY_RPC_PASSWORD,             "" );
250    tr_bencDictAddStr( d, TR_PREFS_KEY_RPC_USERNAME,             "" );
251    tr_bencDictAddStr( d, TR_PREFS_KEY_RPC_WHITELIST,            TR_DEFAULT_RPC_WHITELIST );
252    tr_bencDictAddInt( d, TR_PREFS_KEY_RPC_WHITELIST_ENABLED,    TRUE );
253    tr_bencDictAddInt( d, TR_PREFS_KEY_RPC_PORT,                 atoi( TR_DEFAULT_RPC_PORT_STR ) );
254    tr_bencDictAddInt( d, TR_PREFS_KEY_USPEED,                   100 );
255    tr_bencDictAddInt( d, TR_PREFS_KEY_USPEED_ENABLED,           0 );
256}
257
258void
259tr_sessionGetSettings( tr_session * s, struct tr_benc * d )
260{
261    int i, n=0;
262    char * freeme[16];
263
264    assert( tr_bencIsDict( d ) );
265
266    tr_bencDictReserve( d, 30 );
267    tr_bencDictAddInt( d, TR_PREFS_KEY_BLOCKLIST_ENABLED,        tr_blocklistIsEnabled( s ) );
268    tr_bencDictAddStr( d, TR_PREFS_KEY_DOWNLOAD_DIR,             s->downloadDir );
269    tr_bencDictAddInt( d, TR_PREFS_KEY_DSPEED,                   tr_sessionGetSpeedLimit( s, TR_DOWN ) );
270    tr_bencDictAddInt( d, TR_PREFS_KEY_DSPEED_ENABLED,           tr_sessionIsSpeedLimitEnabled( s, TR_DOWN ) );
271    tr_bencDictAddInt( d, TR_PREFS_KEY_ENCRYPTION,               s->encryptionMode );
272    tr_bencDictAddInt( d, TR_PREFS_KEY_LAZY_BITFIELD,            s->useLazyBitfield );
273    tr_bencDictAddInt( d, TR_PREFS_KEY_MSGLEVEL,                 tr_getMessageLevel( ) );
274    tr_bencDictAddInt( d, TR_PREFS_KEY_OPEN_FILE_LIMIT,          s->openFileLimit );
275    tr_bencDictAddInt( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL,        tr_sessionGetPeerLimit( s ) );
276    tr_bencDictAddInt( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT,       s->peerLimitPerTorrent );
277    tr_bencDictAddInt( d, TR_PREFS_KEY_PEER_PORT,                tr_sessionGetPeerPort( s ) );
278    tr_bencDictAddInt( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ENABLED, s->isPortRandom );
279    tr_bencDictAddInt( d, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW,     s->randomPortLow );
280    tr_bencDictAddInt( d, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH,    s->randomPortHigh );
281    tr_bencDictAddInt( d, TR_PREFS_KEY_PEER_SOCKET_TOS,          s->peerSocketTOS );
282    tr_bencDictAddInt( d, TR_PREFS_KEY_PEX_ENABLED,              s->isPexEnabled );
283    tr_bencDictAddInt( d, TR_PREFS_KEY_PORT_FORWARDING,          tr_sessionIsPortForwardingEnabled( s ) );
284    tr_bencDictAddStr( d, TR_PREFS_KEY_PROXY,                    s->proxy );
285    tr_bencDictAddInt( d, TR_PREFS_KEY_PROXY_AUTH_ENABLED,       s->isProxyAuthEnabled );
286    tr_bencDictAddInt( d, TR_PREFS_KEY_PROXY_ENABLED,            s->isProxyEnabled );
287    tr_bencDictAddStr( d, TR_PREFS_KEY_PROXY_PASSWORD,           s->proxyPassword );
288    tr_bencDictAddInt( d, TR_PREFS_KEY_PROXY_PORT,               s->proxyPort );
289    tr_bencDictAddInt( d, TR_PREFS_KEY_PROXY_TYPE,               s->proxyType );
290    tr_bencDictAddStr( d, TR_PREFS_KEY_PROXY_USERNAME,           s->proxyUsername );
291    tr_bencDictAddInt( d, TR_PREFS_KEY_RPC_AUTH_REQUIRED,        tr_sessionIsRPCPasswordEnabled( s ) );
292    tr_bencDictAddInt( d, TR_PREFS_KEY_RPC_ENABLED,              tr_sessionIsRPCEnabled( s ) );
293    tr_bencDictAddStr( d, TR_PREFS_KEY_RPC_PASSWORD,             freeme[n++] = tr_sessionGetRPCPassword( s ) );
294    tr_bencDictAddInt( d, TR_PREFS_KEY_RPC_PORT,                 tr_sessionGetRPCPort( s ) );
295    tr_bencDictAddStr( d, TR_PREFS_KEY_RPC_USERNAME,             freeme[n++] = tr_sessionGetRPCUsername( s ) );
296    tr_bencDictAddStr( d, TR_PREFS_KEY_RPC_WHITELIST,            freeme[n++] = tr_sessionGetRPCWhitelist( s ) );
297    tr_bencDictAddInt( d, TR_PREFS_KEY_RPC_WHITELIST_ENABLED,    tr_sessionGetRPCWhitelistEnabled( s ) );
298    tr_bencDictAddInt( d, TR_PREFS_KEY_USPEED,                   tr_sessionGetSpeedLimit( s, TR_UP ) );
299    tr_bencDictAddInt( d, TR_PREFS_KEY_USPEED_ENABLED,           tr_sessionIsSpeedLimitEnabled( s, TR_UP ) );
300
301    for( i=0; i<n; ++i )
302        tr_free( freeme[i] );
303}
304
305void
306tr_sessionLoadSettings( tr_benc * d, const char * configDir, const char * appName )
307{
308    char * filename;
309    tr_benc fileSettings;
310
311    assert( tr_bencIsDict( d ) );
312
313    /* get the defaults */
314    tr_sessionGetDefaultSettings( d );
315
316    /* if caller didn't specify a config dir, use the default */
317    if( !configDir || !*configDir )
318        configDir = tr_getDefaultConfigDir( appName );
319
320    /* file settings override the defaults */
321    filename = tr_buildPath( configDir, "settings.json", NULL );
322    if( !tr_bencLoadJSONFile( filename, &fileSettings ) ) {
323        tr_bencMergeDicts( d, &fileSettings );
324        tr_bencFree( &fileSettings );
325    }
326
327    /* cleanup */
328    tr_free( filename );
329}
330
331void
332tr_sessionSaveSettings( tr_session * session, const char * configDir, tr_benc * settings )
333{
334    tr_benc fileSettings;
335    char * filename;
336
337    assert( tr_bencIsDict( settings ) );
338 
339    filename = tr_buildPath( configDir, "settings.json", NULL );
340
341    tr_sessionGetSettings( session, settings );
342
343    if( tr_bencLoadJSONFile( filename, &fileSettings ) ) {
344        tr_bencSaveJSONFile( filename, settings );
345    } else {
346        tr_bencMergeDicts( &fileSettings, settings );
347        tr_bencSaveJSONFile( filename, &fileSettings );
348        tr_bencFree( &fileSettings );
349    }
350
351    tr_inf( "Saved \"%s\"", filename );
352    tr_free( filename );
353}
354
355static void metainfoLookupRescan( tr_session * );
356
357tr_session *
358tr_sessionInit( const char  * tag,
359                const char  * configDir,
360                tr_bool       messageQueuingEnabled,
361                tr_benc     * clientSettings )
362{
363    int64_t i;
364    int64_t j;
365    tr_bool found;
366    const char * str;
367    tr_benc settings;
368    tr_session * session;
369    char * filename;
370    int64_t rpc_enabled, whitelist_enabled, rpc_auth_enabled, rpc_port;
371    const char * whitelist = NULL, *rpc_passwd = NULL, *rpc_username = NULL;
372
373    assert( tr_bencIsDict( clientSettings ) );
374
375    session = tr_new0( tr_session, 1 );
376    session->bandwidth = tr_bandwidthNew( session, NULL );
377    session->lock = tr_lockNew( );
378    session->tag = tr_strdup( tag );
379    dbgmsg( "tr_sessionInit: the session's top-level bandwidth object is %p", session->bandwidth );
380
381    tr_bencInitDict( &settings, 0 );
382    tr_sessionGetDefaultSettings( &settings );
383    tr_bencMergeDicts( &settings, clientSettings );
384
385#ifndef WIN32
386    /* Don't exit when writing on a broken socket */
387    signal( SIGPIPE, SIG_IGN );
388#endif
389
390    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i ); 
391    assert( found ); 
392    session->peerLimitPerTorrent = i; 
393 
394    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_MSGLEVEL, &i ); 
395    assert( found ); 
396    tr_setMessageLevel( i ); 
397    tr_setMessageQueuing( messageQueuingEnabled ); 
398 
399 
400    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEX_ENABLED, &i ); 
401    assert( found ); 
402    session->isPexEnabled = i != 0; 
403 
404    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_ENCRYPTION, &i ); 
405    assert( found ); 
406    session->encryptionMode = i; 
407 
408    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_SOCKET_TOS, &i ); 
409    assert( found ); 
410    session->peerSocketTOS = i; 
411 
412    found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_DOWNLOAD_DIR, &str ); 
413    assert( found ); 
414    session->downloadDir = tr_strdup( str ); 
415 
416    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PROXY_ENABLED, &i ); 
417    assert( found ); 
418    session->isProxyEnabled = i != 0; 
419 
420    found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_PROXY, &str ); 
421    assert( found ); 
422    session->proxy = tr_strdup( str ); 
423 
424    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PROXY_PORT, &i ); 
425    assert( found ); 
426    session->proxyPort = i; 
427 
428    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PROXY_TYPE, &i ); 
429    assert( found ); 
430    session->proxyType = i; 
431 
432    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PROXY_AUTH_ENABLED, &i ); 
433    assert( found ); 
434    session->isProxyAuthEnabled = i != 0; 
435 
436    found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_PROXY_USERNAME, &str ); 
437    assert( found ); 
438    session->proxyUsername = tr_strdup( str ); 
439 
440    found = tr_bencDictFindStr( &settings, TR_PREFS_KEY_PROXY_PASSWORD, &str ); 
441    assert( found ); 
442    session->proxyPassword = tr_strdup( str ); 
443 
444    session->so_sndbuf = 1500 * 3; /* 3x MTU for most ethernet/wireless */ 
445    session->so_rcvbuf = 8192; 
446 
447    tr_setConfigDir( session, configDir ); 
448
449    tr_netInit( ); /* must go before tr_eventInit */
450
451    tr_eventInit( session );
452    while( !session->events )
453        tr_wait( 50 );
454
455    session->peerMgr = tr_peerMgrNew( session );
456
457    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_LAZY_BITFIELD, &i ); 
458    assert( found ); 
459    session->useLazyBitfield = i != 0; 
460
461    /* Initialize rate and file descripts controls */
462
463    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_OPEN_FILE_LIMIT, &i ); 
464    assert( found ); 
465    session->openFileLimit = i;
466    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &j ); 
467    assert( found ); 
468    tr_fdInit( session->openFileLimit, j );
469
470    /**
471    *** random port
472    **/ 
473 
474    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_PORT_RANDOM_ENABLED, &i ); 
475    assert( found ); 
476    session->isPortRandom = i != 0; 
477 
478    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW, &i ); 
479    assert( found ); 
480    session->randomPortLow = i; 
481 
482    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH, &i ); 
483    assert( found ); 
484    session->randomPortHigh = i; 
485 
486    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_PORT_FORWARDING, &i ) 
487         && tr_bencDictFindInt( &settings, TR_PREFS_KEY_PEER_PORT, &j ); 
488    assert( found ); 
489    session->peerPort = session->isPortRandom ? getRandomPort( session ) : j; 
490    session->shared = tr_sharedInit( session, i, session->peerPort ); 
491    session->isPortSet = session->isPortRandom || j>0; 
492
493    /**
494    **/ 
495 
496    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_USPEED, &i )
497         && tr_bencDictFindInt( &settings, TR_PREFS_KEY_USPEED_ENABLED, &j );
498    assert( found ); 
499    tr_sessionSetSpeedLimit( session, TR_UP, i );
500    tr_sessionSetSpeedLimitEnabled( session, TR_UP, j );
501 
502    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_DSPEED, &i )
503         && tr_bencDictFindInt( &settings, TR_PREFS_KEY_DSPEED_ENABLED, &j );
504    assert( found ); 
505    tr_sessionSetSpeedLimit( session, TR_DOWN, i );
506    tr_sessionSetSpeedLimitEnabled( session, TR_DOWN, j );
507 
508    /* first %s is the application name
509       second %s is the version number */
510    tr_inf( _( "%s %s started" ), TR_NAME, LONG_VERSION_STRING );
511
512    /* initialize the blocklist */
513    filename = tr_buildPath( session->configDir, "blocklists", NULL );
514    tr_mkdirp( filename, 0777 );
515    tr_free( filename );
516    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, &i ); 
517    assert( found ); 
518    session->isBlocklistEnabled = i; 
519    loadBlocklists( session ); 
520
521    tr_statsInit( session );
522
523    session->web = tr_webInit( session ); 
524    found = tr_bencDictFindInt( &settings, TR_PREFS_KEY_RPC_ENABLED, &rpc_enabled ) 
525         && tr_bencDictFindInt( &settings, TR_PREFS_KEY_RPC_PORT, &rpc_port ) 
526         && tr_bencDictFindInt( &settings, TR_PREFS_KEY_RPC_WHITELIST_ENABLED, &whitelist_enabled ) 
527         && tr_bencDictFindInt( &settings, TR_PREFS_KEY_RPC_AUTH_REQUIRED, &rpc_auth_enabled ) 
528         && tr_bencDictFindStr( &settings, TR_PREFS_KEY_RPC_WHITELIST, &whitelist ) 
529         && tr_bencDictFindStr( &settings, TR_PREFS_KEY_RPC_USERNAME, &rpc_username ) 
530         && tr_bencDictFindStr( &settings, TR_PREFS_KEY_RPC_PASSWORD, &rpc_passwd ); 
531    assert( found ); 
532    session->rpcServer = tr_rpcInit( session, rpc_enabled, rpc_port, whitelist_enabled, whitelist, 
533                                     rpc_auth_enabled, rpc_username, rpc_passwd ); 
534
535    metainfoLookupRescan( session );
536
537    tr_bencFree( &settings );
538    return session;
539}
540
541/***
542****
543***/
544
545void
546tr_sessionSetDownloadDir( tr_session * session, const char * dir )
547{
548    if( session->downloadDir != dir )
549    {
550        tr_free( session->downloadDir );
551        session->downloadDir = tr_strdup( dir );
552    }
553}
554
555const char *
556tr_sessionGetDownloadDir( const tr_session * session )
557{
558    return session->downloadDir;
559}
560
561/***
562****
563***/
564
565void
566tr_globalLock( tr_session * session )
567{
568    tr_lockLock( session->lock );
569}
570
571void
572tr_globalUnlock( tr_session * session )
573{
574    tr_lockUnlock( session->lock );
575}
576
577tr_bool
578tr_globalIsLocked( const tr_session * session )
579{
580    return session && tr_lockHave( session->lock );
581}
582
583/***********************************************************************
584 * tr_setBindPort
585 ***********************************************************************
586 *
587 **********************************************************************/
588
589struct bind_port_data
590{
591    tr_session * session;
592    tr_port      port;
593};
594
595static void
596tr_setBindPortImpl( void * vdata )
597{
598    struct bind_port_data * data = vdata;
599    tr_session * session = data->session;
600    const tr_port port = data->port;
601
602    session->isPortSet = 1;
603    tr_sharedSetPort( session->shared, port );
604
605    tr_free( data );
606}
607
608static void
609setPortImpl( tr_session * session, tr_port port )
610{
611    struct bind_port_data * data = tr_new( struct bind_port_data, 1 );
612    data->session = session;
613    data->port = port;
614    tr_runInEventThread( session, tr_setBindPortImpl, data );
615}
616
617void
618tr_sessionSetPeerPort( tr_session * session,
619                       tr_port      port )
620{
621    session->isPortRandom = FALSE;
622    session->peerPort = port;
623    setPortImpl( session, session->peerPort );
624}
625
626tr_port
627tr_sessionSetPeerPortRandom( tr_session * session )
628{
629    session->isPortRandom = TRUE;
630    session->peerPort = getRandomPort( session );
631    setPortImpl( session, session->peerPort );
632    return session->peerPort;
633}
634
635tr_port
636tr_sessionGetPeerPort( const tr_session * session )
637{
638    assert( session );
639
640    return session->peerPort;
641}
642
643tr_port_forwarding
644tr_sessionGetPortForwarding( const tr_session * session )
645{
646    return tr_sharedTraversalStatus( session->shared );
647}
648
649/***
650****
651***/
652
653static void
654updateBandwidth( tr_session * session, tr_direction dir )
655{
656    const tr_bool zeroCase = session->speedLimit[dir] < 1 && session->isSpeedLimited[dir];
657
658    tr_bandwidthSetLimited( session->bandwidth, dir, session->isSpeedLimited[dir] && !zeroCase );
659
660    tr_bandwidthSetDesiredSpeed( session->bandwidth, dir, session->speedLimit[dir] );
661}
662
663void
664tr_sessionSetSpeedLimitEnabled( tr_session      * session,
665                                tr_direction      dir,
666                                tr_bool           isLimited )
667{
668    assert( session );
669    assert( tr_isDirection( dir ) );
670
671    session->isSpeedLimited[dir] = isLimited;
672    updateBandwidth( session, dir );
673}
674
675void
676tr_sessionSetSpeedLimit( tr_session    * session,
677                         tr_direction    dir,
678                         int             desiredSpeed )
679{
680    assert( session );
681    assert( tr_isDirection( dir ) );
682
683    session->speedLimit[dir] = desiredSpeed;
684    updateBandwidth( session, dir );
685}
686
687tr_bool
688tr_sessionIsSpeedLimitEnabled( const tr_session  * session,
689                               tr_direction        dir )
690{
691    assert( session );
692    assert( tr_isDirection( dir ) );
693
694    return session->isSpeedLimited[dir];
695}
696
697int
698tr_sessionGetSpeedLimit( const tr_session  * session,
699                         tr_direction        dir )
700{
701    assert( session );
702    assert( tr_isDirection( dir ) );
703
704    return session->speedLimit[dir];
705}
706
707/***
708****
709***/
710
711void
712tr_sessionSetPeerLimit( tr_session * session UNUSED,
713                        uint16_t     maxGlobalPeers )
714{
715    tr_fdSetPeerLimit( maxGlobalPeers );
716}
717
718uint16_t
719tr_sessionGetPeerLimit( const tr_session * session UNUSED )
720{
721    return tr_fdGetPeerLimit( );
722}
723
724void
725tr_sessionSetPeerLimitPerTorrent( tr_session  * session, uint16_t n )
726{
727    session->peerLimitPerTorrent = n;
728}
729
730uint16_t
731tr_sessionGetPeerLimitPerTorrent( const tr_session * session )
732{
733    return session->peerLimitPerTorrent;
734}
735
736/***
737****
738***/
739
740double
741tr_sessionGetPieceSpeed( const tr_session * session, tr_direction dir )
742{
743    return session ? tr_bandwidthGetPieceSpeed( session->bandwidth, 0, dir ) : 0.0;
744}
745
746double
747tr_sessionGetRawSpeed( const tr_session * session, tr_direction dir )
748{
749    return session ? tr_bandwidthGetPieceSpeed( session->bandwidth, 0, dir ) : 0.0;
750}
751
752int
753tr_sessionCountTorrents( const tr_session * session )
754{
755    return session->torrentCount;
756}
757
758static int
759compareTorrentByCur( const void * va, const void * vb )
760{
761    const tr_torrent * a = *(const tr_torrent**)va;
762    const tr_torrent * b = *(const tr_torrent**)vb;
763    const uint64_t     aCur = a->downloadedCur + a->uploadedCur;
764    const uint64_t     bCur = b->downloadedCur + b->uploadedCur;
765
766    if( aCur != bCur )
767        return aCur > bCur ? -1 : 1; /* close the biggest torrents first */
768
769    return 0;
770}
771
772static void
773tr_closeAllConnections( void * vsession )
774{
775    tr_session *  session = vsession;
776    tr_torrent *  tor;
777    int           i, n;
778    tr_torrent ** torrents;
779
780    tr_statsClose( session );
781    tr_sharedShuttingDown( session->shared );
782    tr_rpcClose( &session->rpcServer );
783
784    /* close the torrents.  get the most active ones first so that
785     * if we can't get them all closed in a reasonable amount of time,
786     * at least we get the most important ones first. */
787    tor = NULL;
788    n = session->torrentCount;
789    torrents = tr_new( tr_torrent *, session->torrentCount );
790    for( i = 0; i < n; ++i )
791        torrents[i] = tor = tr_torrentNext( session, tor );
792    qsort( torrents, n, sizeof( tr_torrent* ), compareTorrentByCur );
793    for( i = 0; i < n; ++i )
794        tr_torrentFree( torrents[i] );
795    tr_free( torrents );
796
797    tr_peerMgrFree( session->peerMgr );
798
799    tr_trackerSessionClose( session );
800    tr_list_free( &session->blocklists,
801                  (TrListForeachFunc)_tr_blocklistFree );
802    tr_webClose( &session->web );
803
804    session->isClosed = TRUE;
805}
806
807static int
808deadlineReached( const uint64_t deadline )
809{
810    return tr_date( ) >= deadline;
811}
812
813#define SHUTDOWN_MAX_SECONDS 30
814
815void
816tr_sessionClose( tr_session * session )
817{
818    int            i;
819    const int      maxwait_msec = SHUTDOWN_MAX_SECONDS * 1000;
820    const uint64_t deadline = tr_date( ) + maxwait_msec;
821
822    dbgmsg( "shutting down transmission session %p", session );
823
824    /* close the session */
825    tr_runInEventThread( session, tr_closeAllConnections, session );
826    while( !session->isClosed && !deadlineReached( deadline ) )
827    {
828        dbgmsg(
829            "waiting for the shutdown commands to run in the main thread" );
830        tr_wait( 100 );
831    }
832
833    /* "shared" and "tracker" have live sockets,
834     * so we need to keep the transmission thread alive
835     * for a bit while they tell the router & tracker
836     * that we're closing now */
837    while( ( session->shared
838           || session->tracker ) && !deadlineReached( deadline ) )
839    {
840        dbgmsg( "waiting on port unmap (%p) or tracker (%p)",
841                session->shared, session->tracker );
842        tr_wait( 100 );
843    }
844
845    tr_fdClose( );
846
847    /* close the libtransmission thread */
848    tr_eventClose( session );
849    while( session->events && !deadlineReached( deadline ) )
850    {
851        dbgmsg( "waiting for the libevent thread to shutdown cleanly" );
852        tr_wait( 100 );
853    }
854
855    /* free the session memory */
856    tr_bandwidthFree( session->bandwidth );
857    tr_lockFree( session->lock );
858    for( i = 0; i < session->metainfoLookupCount; ++i )
859        tr_free( session->metainfoLookup[i].filename );
860    tr_free( session->metainfoLookup );
861    tr_free( session->tag );
862    tr_free( session->configDir );
863    tr_free( session->resumeDir );
864    tr_free( session->torrentDir );
865    tr_free( session->downloadDir );
866    tr_free( session->proxy );
867    tr_free( session->proxyUsername );
868    tr_free( session->proxyPassword );
869    tr_free( session );
870}
871
872tr_torrent **
873tr_sessionLoadTorrents( tr_session * session,
874                        tr_ctor    * ctor,
875                        int        * setmeCount )
876{
877    int           i, n = 0;
878    struct stat   sb;
879    DIR *         odir = NULL;
880    const char *  dirname = tr_getTorrentDir( session );
881    tr_torrent ** torrents;
882    tr_list *     l = NULL, *list = NULL;
883
884    tr_ctorSetSave( ctor, FALSE ); /* since we already have them */
885
886    if( !stat( dirname, &sb )
887      && S_ISDIR( sb.st_mode )
888      && ( ( odir = opendir ( dirname ) ) ) )
889    {
890        struct dirent *d;
891        for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
892        {
893            if( d->d_name && d->d_name[0] != '.' ) /* skip dotfiles, ., and ..
894                                                     */
895            {
896                tr_torrent * tor;
897                char * path = tr_buildPath( dirname, d->d_name, NULL );
898                tr_ctorSetMetainfoFromFile( ctor, path );
899                if(( tor = tr_torrentNew( session, ctor, NULL )))
900                {
901                    tr_list_append( &list, tor );
902                    ++n;
903                }
904                tr_free( path );
905            }
906        }
907        closedir( odir );
908    }
909
910    torrents = tr_new( tr_torrent *, n );
911    for( i = 0, l = list; l != NULL; l = l->next )
912        torrents[i++] = (tr_torrent*) l->data;
913    assert( i == n );
914
915    tr_list_free( &list, NULL );
916
917    if( n )
918        tr_inf( _( "Loaded %d torrents" ), n );
919
920    if( setmeCount )
921        *setmeCount = n;
922    return torrents;
923}
924
925/***
926****
927***/
928
929void
930tr_sessionSetPexEnabled( tr_session * session,
931                         tr_bool      enabled )
932{
933    session->isPexEnabled = enabled != 0;
934}
935
936tr_bool
937tr_sessionIsPexEnabled( const tr_session * session )
938{
939    return session->isPexEnabled;
940}
941
942/***
943****
944***/
945
946void
947tr_sessionSetLazyBitfieldEnabled( tr_session * session,
948                                  tr_bool      enabled )
949{
950    session->useLazyBitfield = enabled != 0;
951}
952
953tr_bool
954tr_sessionIsLazyBitfieldEnabled( const tr_session * session )
955{
956    return session->useLazyBitfield;
957}
958
959/***
960****
961***/
962
963void
964tr_sessionSetPortForwardingEnabled( tr_session  * session,
965                                    tr_bool       enabled )
966{
967    tr_globalLock( session );
968    tr_sharedTraversalEnable( session->shared, enabled );
969    tr_globalUnlock( session );
970}
971
972tr_bool
973tr_sessionIsPortForwardingEnabled( const tr_session * session )
974{
975    return tr_sharedTraversalIsEnabled( session->shared );
976}
977
978/***
979****
980***/
981
982int
983tr_blocklistGetRuleCount( const tr_session * session )
984{
985    int       n = 0;
986    tr_list * l;
987
988    for( l = session->blocklists; l; l = l->next )
989        n += _tr_blocklistGetRuleCount( l->data );
990    return n;
991}
992
993tr_bool
994tr_blocklistIsEnabled( const tr_session * session )
995{
996    return session->isBlocklistEnabled;
997}
998
999void
1000tr_blocklistSetEnabled( tr_session * session,
1001                        tr_bool      isEnabled )
1002{
1003    tr_list * l;
1004
1005    session->isBlocklistEnabled = isEnabled != 0;
1006
1007    for( l=session->blocklists; l!=NULL; l=l->next )
1008        _tr_blocklistSetEnabled( l->data, isEnabled );
1009}
1010
1011tr_bool
1012tr_blocklistExists( const tr_session * session )
1013{
1014    return session->blocklists != NULL;
1015}
1016
1017int
1018tr_blocklistSetContent( tr_session * session,
1019                        const char * contentFilename )
1020{
1021    tr_list *      l;
1022    tr_blocklist * b;
1023    const char *   defaultName = "level1.bin";
1024
1025    for( b = NULL, l = session->blocklists; !b && l; l = l->next )
1026        if( tr_stringEndsWith( _tr_blocklistGetFilename( l->data ),
1027                               defaultName ) )
1028            b = l->data;
1029
1030    if( !b )
1031    {
1032        char * path = tr_buildPath( session->configDir, "blocklists", defaultName, NULL );
1033        b = _tr_blocklistNew( path, session->isBlocklistEnabled );
1034        tr_list_append( &session->blocklists, b );
1035        tr_free( path );
1036    }
1037
1038    return _tr_blocklistSetContent( b, contentFilename );
1039}
1040
1041tr_bool
1042tr_sessionIsAddressBlocked( const tr_session * session,
1043                            const tr_address * addr )
1044{
1045    tr_list * l;
1046
1047    for( l = session->blocklists; l; l = l->next )
1048        if( _tr_blocklistHasAddress( l->data, addr ) )
1049            return TRUE;
1050    return FALSE;
1051}
1052
1053/***
1054****
1055***/
1056
1057static int
1058compareLookupEntries( const void * va,
1059                      const void * vb )
1060{
1061    const struct tr_metainfo_lookup * a = va;
1062    const struct tr_metainfo_lookup * b = vb;
1063
1064    return strcmp( a->hashString, b->hashString );
1065}
1066
1067static void
1068metainfoLookupResort( tr_session * session )
1069{
1070    qsort( session->metainfoLookup,
1071           session->metainfoLookupCount,
1072           sizeof( struct tr_metainfo_lookup ),
1073           compareLookupEntries );
1074}
1075
1076static int
1077compareHashStringToLookupEntry( const void * va,
1078                                const void * vb )
1079{
1080    const char *                      a = va;
1081    const struct tr_metainfo_lookup * b = vb;
1082
1083    return strcmp( a, b->hashString );
1084}
1085
1086const char*
1087tr_sessionFindTorrentFile( const tr_session * session,
1088                           const char       * hashStr )
1089{
1090    struct tr_metainfo_lookup * l = bsearch( hashStr,
1091                                             session->metainfoLookup,
1092                                             session->metainfoLookupCount,
1093                                             sizeof( struct tr_metainfo_lookup ),
1094                                             compareHashStringToLookupEntry );
1095
1096    return l ? l->filename : NULL;
1097}
1098
1099static void
1100metainfoLookupRescan( tr_session * session )
1101{
1102    int          i;
1103    int          n;
1104    struct stat  sb;
1105    const char * dirname = tr_getTorrentDir( session );
1106    DIR *        odir = NULL;
1107    tr_ctor *    ctor = NULL;
1108    tr_list *    list = NULL;
1109
1110    /* walk through the directory and find the mappings */
1111    ctor = tr_ctorNew( session );
1112    tr_ctorSetSave( ctor, FALSE ); /* since we already have them */
1113    if( !stat( dirname, &sb ) && S_ISDIR( sb.st_mode ) && ( ( odir = opendir( dirname ) ) ) )
1114    {
1115        struct dirent *d;
1116        for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
1117        {
1118            if( d->d_name && d->d_name[0] != '.' ) /* skip dotfiles, ., and ..
1119                                                     */
1120            {
1121                tr_info inf;
1122                char * path = tr_buildPath( dirname, d->d_name, NULL );
1123                tr_ctorSetMetainfoFromFile( ctor, path );
1124                if( !tr_torrentParse( session, ctor, &inf ) )
1125                {
1126                    tr_list_append( &list, tr_strdup( inf.hashString ) );
1127                    tr_list_append( &list, tr_strdup( path ) );
1128                    tr_metainfoFree( &inf );
1129                }
1130                tr_free( path );
1131            }
1132        }
1133        closedir( odir );
1134    }
1135    tr_ctorFree( ctor );
1136
1137    n = tr_list_size( list ) / 2;
1138    session->metainfoLookup = tr_new0( struct tr_metainfo_lookup, n );
1139    session->metainfoLookupCount = n;
1140    for( i = 0; i < n; ++i )
1141    {
1142        char * hashString = tr_list_pop_front( &list );
1143        char * filename = tr_list_pop_front( &list );
1144
1145        memcpy( session->metainfoLookup[i].hashString, hashString,
1146                2 * SHA_DIGEST_LENGTH + 1 );
1147        tr_free( hashString );
1148        session->metainfoLookup[i].filename = filename;
1149    }
1150
1151    metainfoLookupResort( session );
1152    tr_dbg( "Found %d torrents in \"%s\"", n, dirname );
1153}
1154
1155void
1156tr_sessionSetTorrentFile( tr_session * session,
1157                          const char * hashString,
1158                          const char * filename )
1159{
1160    struct tr_metainfo_lookup * l = bsearch( hashString,
1161                                             session->metainfoLookup,
1162                                             session->metainfoLookupCount,
1163                                             sizeof( struct tr_metainfo_lookup ),
1164                                             compareHashStringToLookupEntry );
1165
1166    if( l )
1167    {
1168        if( l->filename != filename )
1169        {
1170            tr_free( l->filename );
1171            l->filename = tr_strdup( filename );
1172        }
1173    }
1174    else
1175    {
1176        const int n = session->metainfoLookupCount++;
1177        struct tr_metainfo_lookup * node;
1178        session->metainfoLookup = tr_renew( struct tr_metainfo_lookup,
1179                                            session->metainfoLookup,
1180                                            session->metainfoLookupCount );
1181        node = session->metainfoLookup + n;
1182        memcpy( node->hashString, hashString, 2 * SHA_DIGEST_LENGTH + 1 );
1183        node->filename = tr_strdup( filename );
1184        metainfoLookupResort( session );
1185    }
1186}
1187
1188tr_torrent*
1189tr_torrentNext( tr_session * session,
1190                tr_torrent * tor )
1191{
1192    return tor ? tor->next : session->torrentList;
1193}
1194
1195/***
1196****
1197***/
1198
1199void
1200tr_sessionSetRPCEnabled( tr_session * session,
1201                         tr_bool      isEnabled )
1202{
1203    tr_rpcSetEnabled( session->rpcServer, isEnabled );
1204}
1205
1206tr_bool
1207tr_sessionIsRPCEnabled( const tr_session * session )
1208{
1209    return tr_rpcIsEnabled( session->rpcServer );
1210}
1211
1212void
1213tr_sessionSetRPCPort( tr_session * session,
1214                      tr_port      port )
1215{
1216    tr_rpcSetPort( session->rpcServer, port );
1217}
1218
1219tr_port
1220tr_sessionGetRPCPort( const tr_session * session )
1221{
1222    return tr_rpcGetPort( session->rpcServer );
1223}
1224
1225void
1226tr_sessionSetRPCCallback( tr_session * session,
1227                          tr_rpc_func  func,
1228                          void *       user_data )
1229{
1230    session->rpc_func = func;
1231    session->rpc_func_user_data = user_data;
1232}
1233
1234void
1235tr_sessionSetRPCWhitelist( tr_session * session,
1236                           const char * whitelist )
1237{
1238    tr_rpcSetWhitelist( session->rpcServer, whitelist );
1239}
1240
1241char*
1242tr_sessionGetRPCWhitelist( const tr_session * session )
1243{
1244    return tr_rpcGetWhitelist( session->rpcServer );
1245}
1246
1247void
1248tr_sessionSetRPCWhitelistEnabled( tr_session * session,
1249                                  tr_bool      isEnabled )
1250{
1251    tr_rpcSetWhitelistEnabled( session->rpcServer, isEnabled );
1252}
1253
1254tr_bool
1255tr_sessionGetRPCWhitelistEnabled( const tr_session * session )
1256{
1257    return tr_rpcGetWhitelistEnabled( session->rpcServer );
1258}
1259
1260
1261void
1262tr_sessionSetRPCPassword( tr_session * session,
1263                          const char * password )
1264{
1265    tr_rpcSetPassword( session->rpcServer, password );
1266}
1267
1268char*
1269tr_sessionGetRPCPassword( const tr_session * session )
1270{
1271    return tr_rpcGetPassword( session->rpcServer );
1272}
1273
1274void
1275tr_sessionSetRPCUsername( tr_session * session,
1276                          const char * username )
1277{
1278    tr_rpcSetUsername( session->rpcServer, username );
1279}
1280
1281char*
1282tr_sessionGetRPCUsername( const tr_session * session )
1283{
1284    return tr_rpcGetUsername( session->rpcServer );
1285}
1286
1287void
1288tr_sessionSetRPCPasswordEnabled( tr_session * session,
1289                                 tr_bool      isEnabled )
1290{
1291    tr_rpcSetPasswordEnabled( session->rpcServer, isEnabled );
1292}
1293
1294tr_bool
1295tr_sessionIsRPCPasswordEnabled( const tr_session * session )
1296{
1297    return tr_rpcIsPasswordEnabled( session->rpcServer );
1298}
1299
1300/***
1301****
1302***/
1303
1304tr_bool
1305tr_sessionIsProxyEnabled( const tr_session * session )
1306{
1307    return session->isProxyEnabled;
1308}
1309
1310void
1311tr_sessionSetProxyEnabled( tr_session * session,
1312                           tr_bool      isEnabled )
1313{
1314    session->isProxyEnabled = isEnabled != 0;
1315}
1316
1317tr_proxy_type
1318tr_sessionGetProxyType( const tr_session * session )
1319{
1320    return session->proxyType;
1321}
1322
1323void
1324tr_sessionSetProxyType( tr_session *  session,
1325                        tr_proxy_type type )
1326{
1327    session->proxyType = type;
1328}
1329
1330const char*
1331tr_sessionGetProxy( const tr_session * session )
1332{
1333    return session->proxy;
1334}
1335
1336tr_port
1337tr_sessionGetProxyPort( const tr_session * session )
1338{
1339    return session->proxyPort;
1340}
1341
1342void
1343tr_sessionSetProxy( tr_session * session,
1344                    const char * proxy )
1345{
1346    if( proxy != session->proxy )
1347    {
1348        tr_free( session->proxy );
1349        session->proxy = tr_strdup( proxy );
1350    }
1351}
1352
1353void
1354tr_sessionSetProxyPort( tr_session * session,
1355                        tr_port      port )
1356{
1357    session->proxyPort = port;
1358}
1359
1360tr_bool
1361tr_sessionIsProxyAuthEnabled( const tr_session * session )
1362{
1363    return session->isProxyAuthEnabled;
1364}
1365
1366void
1367tr_sessionSetProxyAuthEnabled( tr_session * session,
1368                               tr_bool      isEnabled )
1369{
1370    session->isProxyAuthEnabled = isEnabled != 0;
1371}
1372
1373const char*
1374tr_sessionGetProxyUsername( const tr_session * session )
1375{
1376    return session->proxyUsername;
1377}
1378
1379void
1380tr_sessionSetProxyUsername( tr_session * session,
1381                            const char * username )
1382{
1383    if( username != session->proxyUsername )
1384    {
1385        tr_free( session->proxyUsername );
1386        session->proxyUsername = tr_strdup( username );
1387    }
1388}
1389
1390const char*
1391tr_sessionGetProxyPassword( const tr_session * session )
1392{
1393    return session->proxyPassword;
1394}
1395
1396void
1397tr_sessionSetProxyPassword( tr_session * session,
1398                            const char * password )
1399{
1400    if( password != session->proxyPassword )
1401    {
1402        tr_free( session->proxyPassword );
1403        session->proxyPassword = tr_strdup( password );
1404    }
1405}
1406
Note: See TracBrowser for help on using the repository browser.