source: trunk/libtransmission/session.c @ 8910

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

(trunk libT) fix #2162: .resume file doesn't get saved often enough when its contents change

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