source: trunk/libtransmission/session.c @ 10045

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

(trunk libT) #2823 "crash in bsearch() while updating blocklist" -- fixed in trunk for 1.90

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