source: trunk/libtransmission/session.c @ 9965

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

(trunk) No code changes here... filling in some of the blanks in the "peers" and "utils" doxygen groups' documentation.

  • Property svn:keywords set to Date Rev Author Id
File size: 63.8 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 9965 2010-01-19 19:37:00Z 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    tr_blocklist * b;
1902    const char *   defaultName = "level1.bin";
1903
1904    assert( tr_isSession( 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    return _tr_blocklistSetContent( b, contentFilename );
1920}
1921
1922tr_bool
1923tr_sessionIsAddressBlocked( const tr_session * session,
1924                            const tr_address * addr )
1925{
1926    tr_list * l;
1927
1928    assert( tr_isSession( session ) );
1929
1930    for( l = session->blocklists; l; l = l->next )
1931        if( _tr_blocklistHasAddress( l->data, addr ) )
1932            return TRUE;
1933    return FALSE;
1934}
1935
1936/***
1937****
1938***/
1939
1940static void
1941metainfoLookupInit( tr_session * session )
1942{
1943    struct stat  sb;
1944    const char * dirname = tr_getTorrentDir( session );
1945    DIR *        odir = NULL;
1946    tr_ctor *    ctor = NULL;
1947    tr_benc * lookup;
1948    int n = 0;
1949
1950    assert( tr_isSession( session ) );
1951
1952    /* walk through the directory and find the mappings */
1953    lookup = tr_new0( tr_benc, 1 );
1954    tr_bencInitDict( lookup, 0 );
1955    ctor = tr_ctorNew( session );
1956    tr_ctorSetSave( ctor, FALSE ); /* since we already have them */
1957    if( !stat( dirname, &sb ) && S_ISDIR( sb.st_mode ) && ( ( odir = opendir( dirname ) ) ) )
1958    {
1959        struct dirent *d;
1960        while(( d = readdir( odir )))
1961        {
1962            if( d->d_name && d->d_name[0] != '.' )
1963            {
1964                tr_info inf;
1965                char * path = tr_buildPath( dirname, d->d_name, NULL );
1966                tr_ctorSetMetainfoFromFile( ctor, path );
1967                if( !tr_torrentParse( ctor, &inf ) )
1968                {
1969                    ++n;
1970                    tr_bencDictAddStr( lookup, inf.hashString, path );
1971                }
1972                tr_free( path );
1973            }
1974        }
1975        closedir( odir );
1976    }
1977    tr_ctorFree( ctor );
1978
1979    session->metainfoLookup = lookup;
1980    tr_dbg( "Found %d torrents in \"%s\"", n, dirname );
1981}
1982
1983const char*
1984tr_sessionFindTorrentFile( const tr_session * session,
1985                           const char       * hashString )
1986{
1987    const char * filename = NULL;
1988    if( !session->metainfoLookup )
1989        metainfoLookupInit( (tr_session*)session );
1990    tr_bencDictFindStr( session->metainfoLookup, hashString, &filename );
1991    return filename;
1992}
1993
1994void
1995tr_sessionSetTorrentFile( tr_session * session,
1996                          const char * hashString,
1997                          const char * filename )
1998{
1999    /* since we walk session->configDir/torrents/ to build the lookup table,
2000     * and tr_sessionSetTorrentFile() is just to tell us there's a new file
2001     * in that same directory, we don't need to do anything here if the
2002     * lookup table hasn't been built yet */
2003    if( session->metainfoLookup )
2004        tr_bencDictAddStr( session->metainfoLookup, hashString, filename );
2005}
2006
2007tr_torrent*
2008tr_torrentNext( tr_session * session,
2009                tr_torrent * tor )
2010{
2011    tr_torrent * ret;
2012
2013    assert( !session || tr_isSession( session ) );
2014
2015    if( !session )
2016        ret = NULL;
2017    else if( !tor )
2018        ret = session->torrentList;
2019    else
2020        ret = tor->next;
2021
2022    return ret;
2023}
2024
2025/***
2026****
2027***/
2028
2029void
2030tr_sessionSetRPCEnabled( tr_session * session,
2031                         tr_bool      isEnabled )
2032{
2033    assert( tr_isSession( session ) );
2034
2035    tr_rpcSetEnabled( session->rpcServer, isEnabled );
2036}
2037
2038tr_bool
2039tr_sessionIsRPCEnabled( const tr_session * session )
2040{
2041    assert( tr_isSession( session ) );
2042
2043    return tr_rpcIsEnabled( session->rpcServer );
2044}
2045
2046void
2047tr_sessionSetRPCPort( tr_session * session,
2048                      tr_port      port )
2049{
2050    assert( tr_isSession( session ) );
2051
2052    tr_rpcSetPort( session->rpcServer, port );
2053}
2054
2055tr_port
2056tr_sessionGetRPCPort( const tr_session * session )
2057{
2058    assert( tr_isSession( session ) );
2059
2060    return tr_rpcGetPort( session->rpcServer );
2061}
2062
2063void
2064tr_sessionSetRPCCallback( tr_session * session,
2065                          tr_rpc_func  func,
2066                          void *       user_data )
2067{
2068    assert( tr_isSession( session ) );
2069
2070    session->rpc_func = func;
2071    session->rpc_func_user_data = user_data;
2072}
2073
2074void
2075tr_sessionSetRPCWhitelist( tr_session * session,
2076                           const char * whitelist )
2077{
2078    assert( tr_isSession( session ) );
2079
2080    tr_rpcSetWhitelist( session->rpcServer, whitelist );
2081}
2082
2083const char*
2084tr_sessionGetRPCWhitelist( const tr_session * session )
2085{
2086    assert( tr_isSession( session ) );
2087
2088    return tr_rpcGetWhitelist( session->rpcServer );
2089}
2090
2091void
2092tr_sessionSetRPCWhitelistEnabled( tr_session * session,
2093                                  tr_bool      isEnabled )
2094{
2095    assert( tr_isSession( session ) );
2096
2097    tr_rpcSetWhitelistEnabled( session->rpcServer, isEnabled );
2098}
2099
2100tr_bool
2101tr_sessionGetRPCWhitelistEnabled( const tr_session * session )
2102{
2103    assert( tr_isSession( session ) );
2104
2105    return tr_rpcGetWhitelistEnabled( session->rpcServer );
2106}
2107
2108
2109void
2110tr_sessionSetRPCPassword( tr_session * session,
2111                          const char * password )
2112{
2113    assert( tr_isSession( session ) );
2114
2115    tr_rpcSetPassword( session->rpcServer, password );
2116}
2117
2118const char*
2119tr_sessionGetRPCPassword( const tr_session * session )
2120{
2121    assert( tr_isSession( session ) );
2122
2123    return tr_rpcGetPassword( session->rpcServer );
2124}
2125
2126void
2127tr_sessionSetRPCUsername( tr_session * session,
2128                          const char * username )
2129{
2130    assert( tr_isSession( session ) );
2131
2132    tr_rpcSetUsername( session->rpcServer, username );
2133}
2134
2135const char*
2136tr_sessionGetRPCUsername( const tr_session * session )
2137{
2138    assert( tr_isSession( session ) );
2139
2140    return tr_rpcGetUsername( session->rpcServer );
2141}
2142
2143void
2144tr_sessionSetRPCPasswordEnabled( tr_session * session,
2145                                 tr_bool      isEnabled )
2146{
2147    assert( tr_isSession( session ) );
2148
2149    tr_rpcSetPasswordEnabled( session->rpcServer, isEnabled );
2150}
2151
2152tr_bool
2153tr_sessionIsRPCPasswordEnabled( const tr_session * session )
2154{
2155    assert( tr_isSession( session ) );
2156
2157    return tr_rpcIsPasswordEnabled( session->rpcServer );
2158}
2159
2160const char *
2161tr_sessionGetRPCBindAddress( const tr_session * session )
2162{
2163    assert( tr_isSession( session ) );
2164
2165    return tr_rpcGetBindAddress( session->rpcServer );
2166}
2167
2168/***
2169****
2170***/
2171
2172tr_bool
2173tr_sessionIsProxyEnabled( const tr_session * session )
2174{
2175    assert( tr_isSession( session ) );
2176
2177    return session->isProxyEnabled;
2178}
2179
2180void
2181tr_sessionSetProxyEnabled( tr_session * session,
2182                           tr_bool      isEnabled )
2183{
2184    assert( tr_isSession( session ) );
2185    assert( tr_isBool( isEnabled ) );
2186
2187    session->isProxyEnabled = isEnabled != 0;
2188}
2189
2190tr_proxy_type
2191tr_sessionGetProxyType( const tr_session * session )
2192{
2193    assert( tr_isSession( session ) );
2194
2195    return session->proxyType;
2196}
2197
2198void
2199tr_sessionSetProxyType( tr_session *  session,
2200                        tr_proxy_type type )
2201{
2202    assert( tr_isSession( session ) );
2203
2204    session->proxyType = type;
2205}
2206
2207const char*
2208tr_sessionGetProxy( const tr_session * session )
2209{
2210    assert( tr_isSession( session ) );
2211
2212    return session->proxy;
2213}
2214
2215tr_port
2216tr_sessionGetProxyPort( const tr_session * session )
2217{
2218    assert( tr_isSession( session ) );
2219
2220    return session->proxyPort;
2221}
2222
2223void
2224tr_sessionSetProxy( tr_session * session,
2225                    const char * proxy )
2226{
2227    assert( tr_isSession( session ) );
2228
2229    if( proxy != session->proxy )
2230    {
2231        tr_free( session->proxy );
2232        session->proxy = tr_strdup( proxy );
2233    }
2234}
2235
2236void
2237tr_sessionSetProxyPort( tr_session * session,
2238                        tr_port      port )
2239{
2240    assert( tr_isSession( session ) );
2241
2242    session->proxyPort = port;
2243}
2244
2245tr_bool
2246tr_sessionIsProxyAuthEnabled( const tr_session * session )
2247{
2248    assert( tr_isSession( session ) );
2249
2250    return session->isProxyAuthEnabled;
2251}
2252
2253void
2254tr_sessionSetProxyAuthEnabled( tr_session * session,
2255                               tr_bool      isEnabled )
2256{
2257    assert( tr_isSession( session ) );
2258    assert( tr_isBool( isEnabled ) );
2259
2260    session->isProxyAuthEnabled = isEnabled != 0;
2261}
2262
2263const char*
2264tr_sessionGetProxyUsername( const tr_session * session )
2265{
2266    assert( tr_isSession( session ) );
2267
2268    return session->proxyUsername;
2269}
2270
2271void
2272tr_sessionSetProxyUsername( tr_session * session,
2273                            const char * username )
2274{
2275    assert( tr_isSession( session ) );
2276
2277    if( username != session->proxyUsername )
2278    {
2279        tr_free( session->proxyUsername );
2280        session->proxyUsername = tr_strdup( username );
2281    }
2282}
2283
2284const char*
2285tr_sessionGetProxyPassword( const tr_session * session )
2286{
2287    assert( tr_isSession( session ) );
2288
2289    return session->proxyPassword;
2290}
2291
2292void
2293tr_sessionSetProxyPassword( tr_session * session,
2294                            const char * password )
2295{
2296    assert( tr_isSession( session ) );
2297
2298    if( password != session->proxyPassword )
2299    {
2300        tr_free( session->proxyPassword );
2301        session->proxyPassword = tr_strdup( password );
2302    }
2303}
2304
2305int
2306tr_sessionGetActiveTorrentCount( tr_session * session )
2307{
2308    int ret = 0;
2309    tr_torrent * tor = NULL;
2310
2311    assert( tr_isSession( session ) );
2312
2313    while(( tor = tr_torrentNext( session, tor )))
2314        if( tr_torrentGetActivity( tor ) != TR_STATUS_STOPPED )
2315            ++ret;
2316
2317    return ret;
2318}
Note: See TracBrowser for help on using the repository browser.