source: trunk/libtransmission/session.c @ 9564

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

(trunk libT) fix minor trunk-only memory leak of the tr_session::incompleteDir string

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