source: trunk/libtransmission/session.c @ 8763

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

(trunk, gtk/qt) use Monsoon's strings for encryption preferences and Deluge's system tray tooltip.

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