source: trunk/libtransmission/session.c @ 9849

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

(trunk libT) rename tr_wait() tr_wait_msec()

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