source: trunk/libtransmission/session.c @ 7553

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

(trunk libT) fix a couple of minor memory leaks

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