source: trunk/libtransmission/session.c @ 11583

Last change on this file since 11583 was 11583, checked in by charles, 11 years ago

(trunk) #1408 "total downloading and seeding time per torrent" -- add patch to track how long a torrent has been seeding or downloading

  • Property svn:keywords set to Date Rev Author Id
File size: 69.6 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 11583 2010-12-23 19:32:59Z charles $
11 */
12
13#include <assert.h>
14#include <errno.h> /* ENOENT */
15#include <stdlib.h>
16#include <string.h> /* memcpy */
17
18#include <signal.h>
19#include <sys/types.h> /* stat(), umask() */
20#include <sys/stat.h> /* stat(), umask() */
21#include <unistd.h> /* stat */
22#include <dirent.h> /* opendir */
23
24#include <event.h>
25
26//#define TR_SHOW_DEPRECATED
27#include "transmission.h"
28#include "announcer.h"
29#include "bandwidth.h"
30#include "bencode.h"
31#include "blocklist.h"
32#include "cache.h"
33#include "crypto.h"
34#include "fdlimit.h"
35#include "list.h"
36#include "metainfo.h" /* tr_metainfoFree */
37#include "net.h"
38#include "peer-io.h"
39#include "peer-mgr.h"
40#include "platform.h" /* tr_lock */
41#include "port-forwarding.h"
42#include "rpc-server.h"
43#include "session.h"
44#include "stats.h"
45#include "torrent.h"
46#include "tr-dht.h"
47#include "tr-lpd.h"
48#include "trevent.h"
49#include "utils.h"
50#include "verify.h"
51#include "version.h"
52#include "web.h"
53
54enum
55{
56    SAVE_INTERVAL_SECS = 360,
57
58    DEFAULT_CACHE_SIZE_MB = 4
59};
60
61
62#define dbgmsg( ... ) \
63    do { \
64        if( tr_deepLoggingIsActive( ) ) \
65            tr_deepLog( __FILE__, __LINE__, NULL, __VA_ARGS__ ); \
66    } while( 0 )
67
68static tr_port
69getRandomPort( tr_session * s )
70{
71    return tr_cryptoWeakRandInt( s->randomPortHigh - s->randomPortLow + 1) + s->randomPortLow;
72}
73
74/* Generate a peer id : "-TRxyzb-" + 12 random alphanumeric
75   characters, where x is the major version number, y is the
76   minor version number, z is the maintenance number, and b
77   designates beta (Azureus-style) */
78uint8_t*
79tr_peerIdNew( void )
80{
81    int          i;
82    int          val;
83    int          total = 0;
84    uint8_t *    buf = tr_new( uint8_t, 21 );
85    const char * pool = "0123456789abcdefghijklmnopqrstuvwxyz";
86    const int    base = 36;
87
88    memcpy( buf, PEERID_PREFIX, 8 );
89
90    tr_cryptoRandBuf( buf+8, 11 );
91    for( i=8; i<19; ++i ) {
92        val = buf[i] % base;
93        total += val;
94        buf[i] = pool[val];
95    }
96
97    val = total % base ? base - ( total % base ) : 0;
98    buf[19] = pool[val];
99    buf[20] = '\0';
100
101    return buf;
102}
103
104const uint8_t*
105tr_getPeerId( void )
106{
107    static uint8_t * id = NULL;
108
109    if( id == NULL )
110        id = tr_peerIdNew( );
111    return id;
112}
113
114/***
115****
116***/
117
118tr_encryption_mode
119tr_sessionGetEncryption( tr_session * session )
120{
121    assert( session );
122
123    return session->encryptionMode;
124}
125
126void
127tr_sessionSetEncryption( tr_session *       session,
128                         tr_encryption_mode mode )
129{
130    assert( session );
131    assert( mode == TR_ENCRYPTION_PREFERRED
132          || mode == TR_ENCRYPTION_REQUIRED
133          || mode == TR_CLEAR_PREFERRED );
134
135    session->encryptionMode = mode;
136}
137
138/***
139****
140***/
141
142struct tr_bindinfo
143{
144    int socket;
145    tr_address addr;
146    struct event ev;
147};
148
149
150static void
151close_bindinfo( struct tr_bindinfo * b )
152{
153    if( ( b != NULL ) && ( b->socket >=0 ) )
154    {
155        event_del( &b->ev );
156        tr_netCloseSocket( b->socket );
157    }
158}
159
160static void
161close_incoming_peer_port( tr_session * session )
162{
163    close_bindinfo( session->public_ipv4 );
164    close_bindinfo( session->public_ipv6 );
165}
166
167static void
168free_incoming_peer_port( tr_session * session )
169{
170    close_bindinfo( session->public_ipv4 );
171    tr_free( session->public_ipv4 );
172    session->public_ipv4 = NULL;
173
174    close_bindinfo( session->public_ipv6 );
175    tr_free( session->public_ipv6 );
176    session->public_ipv6 = NULL;
177}
178
179static void
180accept_incoming_peer( int fd, short what UNUSED, void * vsession )
181{
182    int clientSocket;
183    tr_port clientPort;
184    tr_address clientAddr;
185    tr_session * session = vsession;
186
187    clientSocket = tr_netAccept( session, fd, &clientAddr, &clientPort );
188    if( clientSocket > 0 ) {
189        tr_deepLog( __FILE__, __LINE__, NULL, "new incoming connection %d (%s)",
190                   clientSocket, tr_peerIoAddrStr( &clientAddr, clientPort ) );
191        tr_peerMgrAddIncoming( session->peerMgr, &clientAddr, clientPort, clientSocket );
192    }
193}
194
195static void
196open_incoming_peer_port( tr_session * session )
197{
198    struct tr_bindinfo * b;
199
200    /* bind an ipv4 port to listen for incoming peers... */
201    b = session->public_ipv4;
202    b->socket = tr_netBindTCP( &b->addr, session->private_peer_port, FALSE );
203    if( b->socket >= 0 ) {
204        event_set( &b->ev, b->socket, EV_READ | EV_PERSIST, accept_incoming_peer, session );
205        event_add( &b->ev, NULL );
206    }
207
208    /* and do the exact same thing for ipv6, if it's supported... */
209    if( tr_net_hasIPv6( session->private_peer_port ) ) {
210        b = session->public_ipv6;
211        b->socket = tr_netBindTCP( &b->addr, session->private_peer_port, FALSE );
212        if( b->socket >= 0 ) {
213            event_set( &b->ev, b->socket, EV_READ | EV_PERSIST, accept_incoming_peer, session );
214            event_add( &b->ev, NULL );
215        }
216    }
217}
218
219const tr_address*
220tr_sessionGetPublicAddress( const tr_session * session, int tr_af_type )
221{
222    const struct tr_bindinfo * bindinfo;
223
224    switch( tr_af_type )
225    {
226        case TR_AF_INET:  bindinfo = session->public_ipv4; break;
227        case TR_AF_INET6: bindinfo = session->public_ipv6; break;
228        default:          bindinfo = NULL;                 break;
229    }
230
231    return bindinfo ? &bindinfo->addr : NULL;
232}
233
234/***
235****
236***/
237
238#ifdef TR_EMBEDDED
239 #define TR_DEFAULT_ENCRYPTION   TR_CLEAR_PREFERRED
240#else
241 #define TR_DEFAULT_ENCRYPTION   TR_ENCRYPTION_PREFERRED
242#endif
243
244void
245tr_sessionGetDefaultSettings( const char * configDir UNUSED, tr_benc * d )
246{
247    assert( tr_bencIsDict( d ) );
248
249    tr_bencDictReserve( d, 60 );
250    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED,        FALSE );
251    tr_bencDictAddStr ( d, TR_PREFS_KEY_BLOCKLIST_URL,            "http://www.example.com/blocklist" );
252    tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB,        DEFAULT_CACHE_SIZE_MB );
253    tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED,              TRUE );
254    tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED,              FALSE );
255    tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR,             tr_getDefaultDownloadDir( ) );
256    tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps,              100 );
257    tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED,           FALSE );
258    tr_bencDictAddInt ( d, TR_PREFS_KEY_ENCRYPTION,               TR_DEFAULT_ENCRYPTION );
259    tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT,               30 );
260    tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED,       FALSE );
261    tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR,           tr_getDefaultDownloadDir( ) );
262    tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED,   FALSE );
263    tr_bencDictAddBool( d, TR_PREFS_KEY_LAZY_BITFIELD,            TRUE );
264    tr_bencDictAddInt ( d, TR_PREFS_KEY_MSGLEVEL,                 TR_MSG_INF );
265    tr_bencDictAddInt ( d, TR_PREFS_KEY_OPEN_FILE_LIMIT,          atoi( TR_DEFAULT_OPEN_FILE_LIMIT_STR ) );
266    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL,        atoi( TR_DEFAULT_PEER_LIMIT_GLOBAL_STR ) );
267    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT,       atoi( TR_DEFAULT_PEER_LIMIT_TORRENT_STR ) );
268    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT,                atoi( TR_DEFAULT_PEER_PORT_STR ) );
269    tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, FALSE );
270    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW,     49152 );
271    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH,    65535 );
272    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_SOCKET_TOS,          atoi( TR_DEFAULT_PEER_SOCKET_TOS_STR ) );
273    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED,              TRUE );
274    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING,          TRUE );
275    tr_bencDictAddInt ( d, TR_PREFS_KEY_PREALLOCATION,            TR_PREALLOCATE_SPARSE );
276    tr_bencDictAddReal( d, TR_PREFS_KEY_RATIO,                    2.0 );
277    tr_bencDictAddBool( d, TR_PREFS_KEY_RATIO_ENABLED,            FALSE );
278    tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES,     TRUE );
279    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_AUTH_REQUIRED,        FALSE );
280    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_BIND_ADDRESS,         "0.0.0.0" );
281    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_ENABLED,              FALSE );
282    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_PASSWORD,             "" );
283    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_USERNAME,             "" );
284    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_WHITELIST,            TR_DEFAULT_RPC_WHITELIST );
285    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_WHITELIST_ENABLED,    TRUE );
286    tr_bencDictAddInt ( d, TR_PREFS_KEY_RPC_PORT,                 atoi( TR_DEFAULT_RPC_PORT_STR ) );
287    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_URL,                  TR_DEFAULT_RPC_URL_STR );
288    tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, "" );
289    tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, FALSE );
290    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED,        FALSE );
291    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP_KBps,        50 ); /* half the regular */
292    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps,      50 ); /* half the regular */
293    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN,     540 ); /* 9am */
294    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED,   FALSE );
295    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,       1020 ); /* 5pm */
296    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,       TR_SCHED_ALL );
297    tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps,              100 );
298    tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED,           FALSE );
299    tr_bencDictAddInt ( d, TR_PREFS_KEY_UMASK,                    022 );
300    tr_bencDictAddInt ( d, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, 14 );
301    tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV4,        TR_DEFAULT_BIND_ADDRESS_IPV4 );
302    tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV6,        TR_DEFAULT_BIND_ADDRESS_IPV6 );
303    tr_bencDictAddBool( d, TR_PREFS_KEY_START,                    TRUE );
304    tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL,           FALSE );
305}
306
307void
308tr_sessionGetSettings( tr_session * s, struct tr_benc * d )
309{
310    assert( tr_bencIsDict( d ) );
311
312    tr_bencDictReserve( d, 60 );
313    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED,        tr_blocklistIsEnabled( s ) );
314    tr_bencDictAddStr ( d, TR_PREFS_KEY_BLOCKLIST_URL,            tr_blocklistGetURL( s ) );
315    tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB,        tr_sessionGetCacheLimit_MB( s ) );
316    tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED,              s->isDHTEnabled );
317    tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED,              s->isLPDEnabled );
318    tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR,             s->downloadDir );
319    tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps,              tr_sessionGetSpeedLimit_KBps( s, TR_DOWN ) );
320    tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED,           tr_sessionIsSpeedLimited( s, TR_DOWN ) );
321    tr_bencDictAddInt ( d, TR_PREFS_KEY_ENCRYPTION,               s->encryptionMode );
322    tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT,               tr_sessionGetIdleLimit( s ) );
323    tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED,       tr_sessionIsIdleLimited( s ) );
324    tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR,           tr_sessionGetIncompleteDir( s ) );
325    tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED,   tr_sessionIsIncompleteDirEnabled( s ) );
326    tr_bencDictAddBool( d, TR_PREFS_KEY_LAZY_BITFIELD,            s->useLazyBitfield );
327    tr_bencDictAddInt ( d, TR_PREFS_KEY_MSGLEVEL,                 tr_getMessageLevel( ) );
328    tr_bencDictAddInt ( d, TR_PREFS_KEY_OPEN_FILE_LIMIT,          tr_fdGetFileLimit( s ) );
329    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL,        tr_sessionGetPeerLimit( s ) );
330    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT,       s->peerLimitPerTorrent );
331    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT,                tr_sessionGetPeerPort( s ) );
332    tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, s->isPortRandom );
333    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW,     s->randomPortLow );
334    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH,    s->randomPortHigh );
335    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_SOCKET_TOS,          s->peerSocketTOS );
336    if(s->peer_congestion_algorithm && s->peer_congestion_algorithm[0])
337        tr_bencDictAddStr ( d, TR_PREFS_KEY_PEER_CONGESTION_ALGORITHM, s->peer_congestion_algorithm );
338    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED,              s->isPexEnabled );
339    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING,          tr_sessionIsPortForwardingEnabled( s ) );
340    tr_bencDictAddInt ( d, TR_PREFS_KEY_PREALLOCATION,            s->preallocationMode );
341    tr_bencDictAddReal( d, TR_PREFS_KEY_RATIO,                    s->desiredRatio );
342    tr_bencDictAddBool( d, TR_PREFS_KEY_RATIO_ENABLED,            s->isRatioLimited );
343    tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES,     tr_sessionIsIncompleteFileNamingEnabled( s ) );
344    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_AUTH_REQUIRED,        tr_sessionIsRPCPasswordEnabled( s ) );
345    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_BIND_ADDRESS,         tr_sessionGetRPCBindAddress( s ) );
346    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_ENABLED,              tr_sessionIsRPCEnabled( s ) );
347    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_PASSWORD,             tr_sessionGetRPCPassword( s ) );
348    tr_bencDictAddInt ( d, TR_PREFS_KEY_RPC_PORT,                 tr_sessionGetRPCPort( s ) );
349    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_URL,                  tr_sessionGetRPCUrl( s ) );
350    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_USERNAME,             tr_sessionGetRPCUsername( s ) );
351    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_WHITELIST,            tr_sessionGetRPCWhitelist( s ) );
352    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_WHITELIST_ENABLED,    tr_sessionGetRPCWhitelistEnabled( s ) );
353    tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, tr_sessionIsTorrentDoneScriptEnabled( s ) );
354    tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, tr_sessionGetTorrentDoneScript( s ) );
355    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED,        tr_sessionUsesAltSpeed( s ) );
356    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP_KBps,        tr_sessionGetAltSpeed_KBps( s, TR_UP ) );
357    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps,      tr_sessionGetAltSpeed_KBps( s, TR_DOWN ) );
358    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN,     tr_sessionGetAltSpeedBegin( s ) );
359    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED,   tr_sessionUsesAltSpeedTime( s ) );
360    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,       tr_sessionGetAltSpeedEnd( s ) );
361    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,       tr_sessionGetAltSpeedDay( s ) );
362    tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps,              tr_sessionGetSpeedLimit_KBps( s, TR_UP ) );
363    tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED,           tr_sessionIsSpeedLimited( s, TR_UP ) );
364    tr_bencDictAddInt ( d, TR_PREFS_KEY_UMASK,                    s->umask );
365    tr_bencDictAddInt ( d, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, s->uploadSlotsPerTorrent );
366    tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV4,        tr_ntop_non_ts( &s->public_ipv4->addr ) );
367    tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV6,        tr_ntop_non_ts( &s->public_ipv6->addr ) );
368    tr_bencDictAddBool( d, TR_PREFS_KEY_START,                    !tr_sessionGetPaused( s ) );
369    tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL,           tr_sessionGetDeleteSource( s ) );
370}
371
372tr_bool
373tr_sessionLoadSettings( tr_benc * d, const char * configDir, const char * appName )
374{
375    int err = 0;
376    char * filename;
377    tr_benc fileSettings;
378    tr_benc sessionDefaults;
379    tr_benc tmp;
380    tr_bool success = FALSE;
381
382    assert( tr_bencIsDict( d ) );
383
384    /* initializing the defaults: caller may have passed in some app-level defaults.
385     * preserve those and use the session defaults to fill in any missing gaps. */
386    tr_bencInitDict( &sessionDefaults, 0 );
387    tr_sessionGetDefaultSettings( configDir, &sessionDefaults );
388    tr_bencMergeDicts( &sessionDefaults, d );
389    tmp = *d; *d = sessionDefaults; sessionDefaults = tmp;
390
391    /* if caller didn't specify a config dir, use the default */
392    if( !configDir || !*configDir )
393        configDir = tr_getDefaultConfigDir( appName );
394
395    /* file settings override the defaults */
396    filename = tr_buildPath( configDir, "settings.json", NULL );
397    err = tr_bencLoadFile( &fileSettings, TR_FMT_JSON, filename );
398    if( !err ) {
399        tr_bencMergeDicts( d, &fileSettings );
400        tr_bencFree( &fileSettings );
401    }
402
403    /* cleanup */
404    tr_bencFree( &sessionDefaults );
405    tr_free( filename );
406    success = (err==0) || (err==ENOENT);
407    return success;
408}
409
410void
411tr_sessionSaveSettings( tr_session    * session,
412                        const char    * configDir,
413                        const tr_benc * clientSettings )
414{
415    tr_benc settings;
416    char * filename = tr_buildPath( configDir, "settings.json", NULL );
417
418    assert( tr_bencIsDict( clientSettings ) );
419
420    tr_bencInitDict( &settings, 0 );
421
422    /* the existing file settings are the fallback values */
423    {
424        tr_benc fileSettings;
425        const int err = tr_bencLoadFile( &fileSettings, TR_FMT_JSON, filename );
426        if( !err )
427        {
428            tr_bencMergeDicts( &settings, &fileSettings );
429            tr_bencFree( &fileSettings );
430        }
431    }
432
433    /* the client's settings override the file settings */
434    tr_bencMergeDicts( &settings, clientSettings );
435
436    /* the session's true values override the file & client settings */
437    {
438        tr_benc sessionSettings;
439        tr_bencInitDict( &sessionSettings, 0 );
440        tr_sessionGetSettings( session, &sessionSettings );
441        tr_bencMergeDicts( &settings, &sessionSettings );
442        tr_bencFree( &sessionSettings );
443    }
444
445    /* save the result */
446    tr_bencToFile( &settings, TR_FMT_JSON, filename );
447
448    /* cleanup */
449    tr_free( filename );
450    tr_bencFree( &settings );
451}
452
453/***
454****
455***/
456
457/**
458 * Periodically save the .resume files of any torrents whose
459 * status has recently changed.  This prevents loss of metadata
460 * in the case of a crash, unclean shutdown, clumsy user, etc.
461 */
462static void
463onSaveTimer( int foo UNUSED, short bar UNUSED, void * vsession )
464{
465    tr_torrent * tor = NULL;
466    tr_session * session = vsession;
467
468    if( tr_cacheFlushDone( session->cache ) )
469        tr_err( "Error while flushing completed pieces from cache" );
470
471    while(( tor = tr_torrentNext( session, tor )))
472        tr_torrentSave( tor );
473
474    tr_statsSaveDirty( session );
475
476    tr_timerAdd( session->saveTimer, SAVE_INTERVAL_SECS, 0 );
477}
478
479/***
480****
481***/
482
483static void tr_sessionInitImpl( void * );
484
485struct init_data
486{
487    tr_session  * session;
488    const char  * configDir;
489    tr_bool       done;
490    tr_bool       messageQueuingEnabled;
491    tr_benc     * clientSettings;
492};
493
494tr_session *
495tr_sessionInit( const char  * tag,
496                const char  * configDir,
497                tr_bool       messageQueuingEnabled,
498                tr_benc     * clientSettings )
499{
500    int64_t i;
501    tr_session * session;
502    struct init_data data;
503
504    assert( tr_bencIsDict( clientSettings ) );
505
506    tr_timeUpdate( time( NULL ) );
507
508    /* initialize the bare skeleton of the session object */
509    session = tr_new0( tr_session, 1 );
510    session->bandwidth = tr_bandwidthNew( session, NULL );
511    session->lock = tr_lockNew( );
512    session->cache = tr_cacheNew( 1024*1024*2 );
513    session->tag = tr_strdup( tag );
514    session->magicNumber = SESSION_MAGIC_NUMBER;
515    session->buffer = tr_valloc( SESSION_BUFFER_SIZE );
516    tr_bencInitList( &session->removedTorrents, 0 );
517
518    /* nice to start logging at the very beginning */
519    if( tr_bencDictFindInt( clientSettings, TR_PREFS_KEY_MSGLEVEL, &i ) )
520        tr_setMessageLevel( i );
521
522    /* start the libtransmission thread */
523    tr_netInit( ); /* must go before tr_eventInit */
524    tr_eventInit( session );
525    assert( session->events != NULL );
526
527    /* run the rest in the libtransmission thread */
528    data.done = FALSE;
529    data.session = session;
530    data.configDir = configDir;
531    data.messageQueuingEnabled = messageQueuingEnabled;
532    data.clientSettings = clientSettings;
533    tr_runInEventThread( session, tr_sessionInitImpl, &data );
534    while( !data.done )
535        tr_wait_msec( 100 );
536
537    return session;
538}
539
540static void turtleCheckClock( tr_session * s, struct tr_turtle_info * t );
541
542static void
543onNowTimer( int foo UNUSED, short bar UNUSED, void * vsession )
544{
545    int usec;
546    const int min = 100;
547    const int max = 999999;
548    struct timeval tv;
549    tr_torrent * tor = NULL;
550    tr_session * session = vsession;
551
552    assert( tr_isSession( session ) );
553    assert( session->nowTimer != NULL );
554
555    /**
556    ***  tr_session things to do once per second
557    **/
558
559    tr_timeUpdate( time( NULL ) );
560
561    if( session->turtle.isClockEnabled )
562        turtleCheckClock( session, &session->turtle );
563
564    while(( tor = tr_torrentNext( session, tor ))) {
565        if( tor->isRunning ) {
566            if( tr_torrentIsSeed( tor ) )
567                ++tor->secondsSeeding;
568            else
569                ++tor->secondsDownloading;
570        }
571    }
572
573    /**
574    ***  Set the timer
575    **/
576
577    /* schedule the next timer for right after the next second begins */
578    gettimeofday( &tv, NULL );
579    usec = 1000000 - tv.tv_usec;
580    if( usec > max ) usec = max;
581    if( usec < min ) usec = min;
582    tr_timerAdd( session->nowTimer, 0, usec );
583    /* fprintf( stderr, "time %zu sec, %zu microsec\n", (size_t)tr_time(), (size_t)tv.tv_usec ); */
584}
585
586static void loadBlocklists( tr_session * session );
587
588static void
589tr_sessionInitImpl( void * vdata )
590{
591    tr_benc settings;
592    struct init_data * data = vdata;
593    tr_benc * clientSettings = data->clientSettings;
594    tr_session * session = data->session;
595
596    assert( tr_amInEventThread( session ) );
597    assert( tr_bencIsDict( clientSettings ) );
598
599    dbgmsg( "tr_sessionInit: the session's top-level bandwidth object is %p",
600            session->bandwidth );
601
602    tr_bencInitDict( &settings, 0 );
603    tr_sessionGetDefaultSettings( data->configDir, &settings );
604    tr_bencMergeDicts( &settings, clientSettings );
605
606    session->nowTimer = tr_new0( struct event, 1 );
607    evtimer_set( session->nowTimer, onNowTimer, session );
608    onNowTimer( 0, 0, session );
609
610#ifndef WIN32
611    /* Don't exit when writing on a broken socket */
612    signal( SIGPIPE, SIG_IGN );
613#endif
614
615    tr_setMessageQueuing( data->messageQueuingEnabled );
616
617    tr_setConfigDir( session, data->configDir );
618
619    session->peerMgr = tr_peerMgrNew( session );
620
621    session->shared = tr_sharedInit( session );
622
623    /**
624    ***  Blocklist
625    **/
626
627    {
628        char * filename = tr_buildPath( session->configDir, "blocklists", NULL );
629        tr_mkdirp( filename, 0777 );
630        tr_free( filename );
631        loadBlocklists( session );
632    }
633
634    assert( tr_isSession( session ) );
635
636    session->saveTimer = tr_new0( struct event, 1 );
637    evtimer_set( session->saveTimer, onSaveTimer, session );
638    tr_timerAdd( session->saveTimer, SAVE_INTERVAL_SECS, 0 );
639
640    tr_announcerInit( session );
641
642    /* first %s is the application name
643       second %s is the version number */
644    tr_inf( _( "%s %s started" ), TR_NAME, LONG_VERSION_STRING );
645
646    tr_statsInit( session );
647
648    tr_webInit( session );
649
650    tr_sessionSet( session, &settings );
651
652    if( session->isDHTEnabled )
653        tr_dhtInit( session, &session->public_ipv4->addr );
654
655    if( session->isLPDEnabled )
656        tr_lpdInit( session, &session->public_ipv4->addr );
657
658    /* cleanup */
659    tr_bencFree( &settings );
660    data->done = TRUE;
661}
662
663static void turtleBootstrap( tr_session *, struct tr_turtle_info * );
664static void setPeerPort( tr_session * session, tr_port port );
665
666static void
667sessionSetImpl( void * vdata )
668{
669    int64_t i;
670    double  d;
671    tr_bool boolVal;
672    const char * str;
673    struct tr_bindinfo b;
674    struct init_data * data = vdata;
675    tr_session * session = data->session;
676    tr_benc * settings = data->clientSettings;
677    struct tr_turtle_info * turtle = &session->turtle;
678
679    assert( tr_isSession( session ) );
680    assert( tr_bencIsDict( settings ) );
681    assert( tr_amInEventThread( session ) );
682
683    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_MSGLEVEL, &i ) )
684        tr_setMessageLevel( i );
685
686    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_UMASK, &i ) ) {
687        session->umask = (mode_t)i;
688        umask( session->umask );
689    }
690
691    /* misc features */
692    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_MAX_CACHE_SIZE_MB, &i ) )
693        tr_sessionSetCacheLimit_MB( session, i );
694    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_LAZY_BITFIELD, &boolVal ) )
695        tr_sessionSetLazyBitfieldEnabled( session, boolVal );
696    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_PEER_LIMIT_TORRENT, &i ) )
697        tr_sessionSetPeerLimitPerTorrent( session, i );
698    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_PEX_ENABLED, &boolVal ) )
699        tr_sessionSetPexEnabled( session, boolVal );
700    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_DHT_ENABLED, &boolVal ) )
701        tr_sessionSetDHTEnabled( session, boolVal );
702    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_LPD_ENABLED, &boolVal ) )
703        tr_sessionSetLPDEnabled( session, boolVal );
704    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ENCRYPTION, &i ) )
705        tr_sessionSetEncryption( session, i );
706    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_PEER_SOCKET_TOS, &i ) )
707        session->peerSocketTOS = i;
708    if( tr_bencDictFindStr( settings, TR_PREFS_KEY_PEER_CONGESTION_ALGORITHM, &str ) )
709        session->peer_congestion_algorithm = tr_strdup(str);
710    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_BLOCKLIST_ENABLED, &boolVal ) )
711        tr_blocklistSetEnabled( session, boolVal );
712    if( tr_bencDictFindStr( settings, TR_PREFS_KEY_BLOCKLIST_URL, &str ) )
713        tr_blocklistSetURL( session, str );
714    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_START, &boolVal ) )
715        tr_sessionSetPaused( session, !boolVal );
716    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_TRASH_ORIGINAL, &boolVal) )
717        tr_sessionSetDeleteSource( session, boolVal );
718
719    /* files and directories */
720    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_PREALLOCATION, &i ) )
721        session->preallocationMode = i;
722    if( tr_bencDictFindStr( settings, TR_PREFS_KEY_DOWNLOAD_DIR, &str ) )
723        tr_sessionSetDownloadDir( session, str );
724    if( tr_bencDictFindStr( settings, TR_PREFS_KEY_INCOMPLETE_DIR, &str ) )
725        tr_sessionSetIncompleteDir( session, str );
726    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED, &boolVal ) )
727        tr_sessionSetIncompleteDirEnabled( session, boolVal );
728    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_RENAME_PARTIAL_FILES, &boolVal ) )
729        tr_sessionSetIncompleteFileNamingEnabled( session, boolVal );
730
731    /* rpc server */
732    if( session->rpcServer != NULL ) /* close the old one */
733        tr_rpcClose( &session->rpcServer );
734    session->rpcServer = tr_rpcInit( session, settings );
735
736    /* public addresses */
737
738    free_incoming_peer_port( session );
739
740    str = TR_PREFS_KEY_BIND_ADDRESS_IPV4;
741    tr_bencDictFindStr( settings, TR_PREFS_KEY_BIND_ADDRESS_IPV4, &str );
742    if( !tr_pton( str, &b.addr ) || ( b.addr.type != TR_AF_INET ) )
743        b.addr = tr_inaddr_any;
744    b.socket = -1;
745    session->public_ipv4 = tr_memdup( &b, sizeof( struct tr_bindinfo ) );
746
747    str = TR_PREFS_KEY_BIND_ADDRESS_IPV6;
748    tr_bencDictFindStr( settings, TR_PREFS_KEY_BIND_ADDRESS_IPV6, &str );
749    if( !tr_pton( str, &b.addr ) || ( b.addr.type != TR_AF_INET6 ) )
750        b.addr = tr_in6addr_any;
751    b.socket = -1;
752    session->public_ipv6 = tr_memdup( &b, sizeof( struct tr_bindinfo ) );
753
754    /* incoming peer port */
755    if( tr_bencDictFindInt ( settings, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW, &i ) )
756        session->randomPortLow = i;
757    if( tr_bencDictFindInt ( settings, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH, &i ) )
758        session->randomPortHigh = i;
759    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, &boolVal ) )
760        tr_sessionSetPeerPortRandomOnStart( session, boolVal );
761    if( !tr_bencDictFindInt( settings, TR_PREFS_KEY_PEER_PORT, &i ) )
762        i = session->private_peer_port;
763    setPeerPort( session, boolVal ? getRandomPort( session ) : i );
764    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_PORT_FORWARDING, &boolVal ) )
765        tr_sessionSetPortForwardingEnabled( session, boolVal );
766
767    /* file and peer socket limits */
768    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_PEER_LIMIT_GLOBAL, &i ) )
769        tr_fdSetPeerLimit( session, i );
770    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_OPEN_FILE_LIMIT, &i ) )
771        tr_fdSetFileLimit( session, i );
772
773    /**
774    **/
775
776    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, &i ) )
777        session->uploadSlotsPerTorrent = i;
778
779    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_USPEED_KBps, &i ) )
780        tr_sessionSetSpeedLimit_KBps( session, TR_UP, i );
781    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_USPEED_ENABLED, &boolVal ) )
782        tr_sessionLimitSpeed( session, TR_UP, boolVal );
783
784    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_DSPEED_KBps, &i ) )
785        tr_sessionSetSpeedLimit_KBps( session, TR_DOWN, i );
786    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_DSPEED_ENABLED, &boolVal ) )
787        tr_sessionLimitSpeed( session, TR_DOWN, boolVal );
788
789    if( tr_bencDictFindReal( settings, TR_PREFS_KEY_RATIO, &d ) )
790        tr_sessionSetRatioLimit( session, d );
791    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_RATIO_ENABLED, &boolVal ) )
792        tr_sessionSetRatioLimited( session, boolVal );
793
794    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_IDLE_LIMIT, &i ) )
795        tr_sessionSetIdleLimit( session, i );
796    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_IDLE_LIMIT_ENABLED, &boolVal ) )
797        tr_sessionSetIdleLimited( session, boolVal );
798
799    /**
800    ***  Turtle Mode
801    **/
802
803    /* update the turtle mode's fields */
804    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ALT_SPEED_UP_KBps, &i ) )
805        turtle->speedLimit_Bps[TR_UP] = toSpeedBytes( i );
806    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps, &i ) )
807        turtle->speedLimit_Bps[TR_DOWN] = toSpeedBytes( i );
808    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN, &i ) )
809        turtle->beginMinute = i;
810    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ALT_SPEED_TIME_END, &i ) )
811        turtle->endMinute = i;
812    if( tr_bencDictFindInt( settings, TR_PREFS_KEY_ALT_SPEED_TIME_DAY, &i ) )
813        turtle->days = i;
814    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED, &boolVal ) )
815        turtle->isClockEnabled = boolVal;
816    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_ALT_SPEED_ENABLED, &boolVal ) )
817        turtle->isEnabled = boolVal;
818    turtleBootstrap( session, turtle );
819
820    /**
821    ***  Scripts
822    **/
823
824    if( tr_bencDictFindBool( settings, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, &boolVal ) )
825        tr_sessionSetTorrentDoneScriptEnabled( session, boolVal );
826    if( tr_bencDictFindStr( settings, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, &str ) )
827        tr_sessionSetTorrentDoneScript( session, str );
828
829    data->done = TRUE;
830}
831
832void
833tr_sessionSet( tr_session * session, struct tr_benc  * settings )
834{
835    struct init_data data;
836    data.done = FALSE;
837    data.session = session;
838    data.clientSettings = settings;
839
840    /* run the rest in the libtransmission thread */
841    tr_runInEventThread( session, sessionSetImpl, &data );
842    while( !data.done )
843        tr_wait_msec( 100 );
844}
845
846/***
847****
848***/
849
850void
851tr_sessionSetDownloadDir( tr_session * session, const char * dir )
852{
853    assert( tr_isSession( session ) );
854
855    if( session->downloadDir != dir )
856    {
857        tr_free( session->downloadDir );
858        session->downloadDir = tr_strdup( dir );
859    }
860}
861
862const char *
863tr_sessionGetDownloadDir( const tr_session * session )
864{
865    assert( tr_isSession( session ) );
866
867    return session->downloadDir;
868}
869
870/***
871****
872***/
873
874void
875tr_sessionSetIncompleteFileNamingEnabled( tr_session * session, tr_bool b )
876{
877    assert( tr_isSession( session ) );
878    assert( tr_isBool( b ) );
879
880    session->isIncompleteFileNamingEnabled = b;
881}
882
883tr_bool
884tr_sessionIsIncompleteFileNamingEnabled( const tr_session * session )
885{
886    assert( tr_isSession( session ) );
887
888    return session->isIncompleteFileNamingEnabled;
889}
890
891/***
892****
893***/
894
895
896void
897tr_sessionSetIncompleteDir( tr_session * session, const char * dir )
898{
899    assert( tr_isSession( session ) );
900
901    if( session->incompleteDir != dir )
902    {
903        tr_free( session->incompleteDir );
904
905        session->incompleteDir = tr_strdup( dir );
906    }
907}
908
909const char*
910tr_sessionGetIncompleteDir( const tr_session * session )
911{
912    assert( tr_isSession( session ) );
913
914    return session->incompleteDir;
915}
916
917void
918tr_sessionSetIncompleteDirEnabled( tr_session * session, tr_bool b )
919{
920    assert( tr_isSession( session ) );
921    assert( tr_isBool( b ) );
922
923    session->isIncompleteDirEnabled = b;
924}
925
926tr_bool
927tr_sessionIsIncompleteDirEnabled( const tr_session * session )
928{
929    assert( tr_isSession( session ) );
930
931    return session->isIncompleteDirEnabled;
932}
933
934/***
935****
936***/
937
938void*
939tr_sessionGetBuffer( tr_session * session )
940{
941    assert( tr_isSession( session ) );
942    assert( !session->bufferInUse );
943    assert( tr_amInEventThread( session ) );
944
945    session->bufferInUse = TRUE;
946    return session->buffer;
947}
948
949void
950tr_sessionReleaseBuffer( tr_session * session )
951{
952    assert( tr_isSession( session ) );
953    assert( session->bufferInUse );
954    assert( tr_amInEventThread( session ) );
955
956    session->bufferInUse = FALSE;
957}
958
959void
960tr_sessionLock( tr_session * session )
961{
962    assert( tr_isSession( session ) );
963
964    tr_lockLock( session->lock );
965}
966
967void
968tr_sessionUnlock( tr_session * session )
969{
970    assert( tr_isSession( session ) );
971
972    tr_lockUnlock( session->lock );
973}
974
975tr_bool
976tr_sessionIsLocked( const tr_session * session )
977{
978    return tr_isSession( session ) && tr_lockHave( session->lock );
979}
980
981/***********************************************************************
982 * tr_setBindPort
983 ***********************************************************************
984 *
985 **********************************************************************/
986
987static void
988peerPortChanged( void * session )
989{
990    tr_torrent * tor = NULL;
991
992    assert( tr_isSession( session ) );
993
994    close_incoming_peer_port( session );
995    open_incoming_peer_port( session );
996    tr_sharedPortChanged( session );
997
998    while(( tor = tr_torrentNext( session, tor )))
999        tr_torrentChangeMyPort( tor );
1000}
1001
1002static void
1003setPeerPort( tr_session * session, tr_port port )
1004{
1005    session->private_peer_port = port;
1006    session->public_peer_port = port;
1007
1008    tr_runInEventThread( session, peerPortChanged, session );
1009}
1010
1011void
1012tr_sessionSetPeerPort( tr_session * session, tr_port port )
1013{
1014    if( tr_isSession( session ) && ( session->private_peer_port != port ) )
1015    {
1016        setPeerPort( session, port );
1017    }
1018}
1019
1020tr_port
1021tr_sessionGetPeerPort( const tr_session * session )
1022{
1023    return tr_isSession( session ) ? session->private_peer_port : 0;
1024}
1025
1026tr_port
1027tr_sessionSetPeerPortRandom( tr_session * session )
1028{
1029    assert( tr_isSession( session ) );
1030
1031    tr_sessionSetPeerPort( session, getRandomPort( session ) );
1032    return session->private_peer_port;
1033}
1034
1035void
1036tr_sessionSetPeerPortRandomOnStart( tr_session * session,
1037                                    tr_bool random )
1038{
1039    assert( tr_isSession( session ) );
1040
1041    session->isPortRandom = random;
1042}
1043
1044tr_bool
1045tr_sessionGetPeerPortRandomOnStart( tr_session * session )
1046{
1047    assert( tr_isSession( session ) );
1048
1049    return session->isPortRandom;
1050}
1051
1052tr_port_forwarding
1053tr_sessionGetPortForwarding( const tr_session * session )
1054{
1055    assert( tr_isSession( session ) );
1056
1057    return tr_sharedTraversalStatus( session->shared );
1058}
1059
1060/***
1061****
1062***/
1063
1064void
1065tr_sessionSetRatioLimited( tr_session * session, tr_bool isLimited )
1066{
1067    assert( tr_isSession( session ) );
1068
1069    session->isRatioLimited = isLimited;
1070}
1071
1072void
1073tr_sessionSetRatioLimit( tr_session * session, double desiredRatio )
1074{
1075    assert( tr_isSession( session ) );
1076
1077    session->desiredRatio = desiredRatio;
1078}
1079
1080tr_bool
1081tr_sessionIsRatioLimited( const tr_session  * session )
1082{
1083    assert( tr_isSession( session ) );
1084
1085    return session->isRatioLimited;
1086}
1087
1088double
1089tr_sessionGetRatioLimit( const tr_session * session )
1090{
1091    assert( tr_isSession( session ) );
1092
1093    return session->desiredRatio;
1094}
1095
1096/***
1097****
1098***/
1099
1100void
1101tr_sessionSetIdleLimited( tr_session * session, tr_bool isLimited )
1102{
1103    assert( tr_isSession( session ) );
1104
1105    session->isIdleLimited = isLimited;
1106}
1107
1108void
1109tr_sessionSetIdleLimit( tr_session * session, uint16_t idleMinutes )
1110{
1111    assert( tr_isSession( session ) );
1112
1113    session->idleLimitMinutes = idleMinutes;
1114}
1115
1116tr_bool
1117tr_sessionIsIdleLimited( const tr_session  * session )
1118{
1119    assert( tr_isSession( session ) );
1120
1121    return session->isIdleLimited;
1122}
1123
1124uint16_t
1125tr_sessionGetIdleLimit( const tr_session * session )
1126{
1127    assert( tr_isSession( session ) );
1128
1129    return session->idleLimitMinutes;
1130}
1131
1132/***
1133****
1134****  SPEED LIMITS
1135****
1136***/
1137
1138tr_bool
1139tr_sessionGetActiveSpeedLimit_Bps( const tr_session * session, tr_direction dir, int * setme_Bps )
1140{
1141    int isLimited = TRUE;
1142
1143    if( !tr_isSession( session ) )
1144        return FALSE;
1145
1146    if( tr_sessionUsesAltSpeed( session ) )
1147        *setme_Bps = tr_sessionGetAltSpeed_Bps( session, dir );
1148    else if( tr_sessionIsSpeedLimited( session, dir ) )
1149        *setme_Bps = tr_sessionGetSpeedLimit_Bps( session, dir );
1150    else
1151        isLimited = FALSE;
1152
1153    return isLimited;
1154}
1155tr_bool
1156tr_sessionGetActiveSpeedLimit_KBps( const tr_session  * session,
1157                                    tr_direction        dir,
1158                                    double            * setme_KBps )
1159{
1160    int Bps = 0;
1161    const tr_bool is_active = tr_sessionGetActiveSpeedLimit_Bps( session, dir, &Bps );
1162    *setme_KBps = toSpeedKBps( Bps );
1163    return is_active;
1164}
1165
1166static void
1167updateBandwidth( tr_session * session, tr_direction dir )
1168{
1169    int limit_Bps = 0;
1170    const tr_bool isLimited = tr_sessionGetActiveSpeedLimit_Bps( session, dir, &limit_Bps );
1171    const tr_bool zeroCase = isLimited && !limit_Bps;
1172
1173    tr_bandwidthSetLimited( session->bandwidth, dir, isLimited && !zeroCase );
1174
1175    tr_bandwidthSetDesiredSpeed_Bps( session->bandwidth, dir, limit_Bps );
1176}
1177
1178enum
1179{
1180    MINUTES_PER_HOUR = 60,
1181    MINUTES_PER_DAY = MINUTES_PER_HOUR * 24,
1182    MINUTES_PER_WEEK = MINUTES_PER_DAY * 7
1183};
1184
1185static void
1186turtleUpdateTable( struct tr_turtle_info * t )
1187{
1188    int day;
1189    tr_bitfield * b = &t->minutes;
1190
1191    tr_bitfieldClear( b );
1192
1193    for( day=0; day<7; ++day )
1194    {
1195        if( t->days & (1<<day) )
1196        {
1197            int i;
1198            const time_t begin = t->beginMinute;
1199            time_t end = t->endMinute;
1200
1201            if( end <= begin )
1202                end += MINUTES_PER_DAY;
1203
1204            for( i=begin; i<end; ++i )
1205                tr_bitfieldAdd( b, (i+day*MINUTES_PER_DAY) % MINUTES_PER_WEEK );
1206        }
1207    }
1208}
1209
1210static void
1211altSpeedToggled( void * vsession )
1212{
1213    tr_session * session = vsession;
1214    struct tr_turtle_info * t = &session->turtle;
1215
1216    assert( tr_isSession( session ) );
1217
1218    updateBandwidth( session, TR_UP );
1219    updateBandwidth( session, TR_DOWN );
1220
1221    if( t->callback != NULL )
1222        (*t->callback)( session, t->isEnabled, t->changedByUser, t->callbackUserData );
1223}
1224
1225static void
1226useAltSpeed( tr_session * s, struct tr_turtle_info * t,
1227             tr_bool enabled, tr_bool byUser )
1228{
1229    assert( tr_isSession( s ) );
1230    assert( t != NULL );
1231    assert( tr_isBool( enabled ) );
1232    assert( tr_isBool( byUser ) );
1233
1234    if( t->isEnabled != enabled )
1235    {
1236        t->isEnabled = enabled;
1237        t->changedByUser = byUser;
1238        tr_runInEventThread( s, altSpeedToggled, s );
1239    }
1240}
1241
1242/**
1243 * @param enabled whether turtle should be on/off according to the scheduler
1244 * @param changed whether that's different from the previous minute
1245 */
1246static void
1247testTurtleTime( const struct tr_turtle_info * t,
1248                tr_bool * enabled,
1249                tr_bool * changed )
1250{
1251    tr_bool e;
1252    struct tm tm;
1253    size_t minute_of_the_week;
1254    const time_t now = tr_time( );
1255
1256    tr_localtime_r( &now, &tm );
1257
1258    minute_of_the_week = tm.tm_wday * MINUTES_PER_DAY
1259                       + tm.tm_hour * MINUTES_PER_HOUR
1260                       + tm.tm_min;
1261    if( minute_of_the_week >= MINUTES_PER_WEEK ) /* leap minutes? */
1262        minute_of_the_week = MINUTES_PER_WEEK - 1;
1263
1264    e = tr_bitfieldHasFast( &t->minutes, minute_of_the_week );
1265    if( enabled != NULL )
1266        *enabled = e;
1267
1268    if( changed != NULL )
1269    {
1270        const size_t prev = minute_of_the_week > 0 ? minute_of_the_week - 1
1271                                                   : MINUTES_PER_WEEK - 1;
1272        *changed = e != tr_bitfieldHasFast( &t->minutes, prev );
1273    }
1274}
1275
1276static void
1277turtleCheckClock( tr_session * s, struct tr_turtle_info * t )
1278{
1279    tr_bool enabled;
1280    tr_bool changed;
1281
1282    assert( t->isClockEnabled );
1283
1284    testTurtleTime( t, &enabled, &changed );
1285
1286    if( changed )
1287    {
1288        tr_inf( "Time to turn %s turtle mode!", (enabled?"on":"off") );
1289        useAltSpeed( s, t, enabled, FALSE );
1290    }
1291}
1292
1293/* Called after the turtle's fields are loaded from an outside source.
1294 * It initializes the implementation fields
1295 * and turns on turtle mode if the clock settings say to. */
1296static void
1297turtleBootstrap( tr_session * session, struct tr_turtle_info * turtle )
1298{
1299    turtle->changedByUser = FALSE;
1300
1301    tr_bitfieldConstruct( &turtle->minutes, MINUTES_PER_WEEK );
1302
1303    turtleUpdateTable( turtle );
1304
1305    if( turtle->isClockEnabled )
1306        testTurtleTime( turtle, &turtle->isEnabled, NULL );
1307
1308    altSpeedToggled( session );
1309}
1310
1311/***
1312****  Primary session speed limits
1313***/
1314
1315void
1316tr_sessionSetSpeedLimit_Bps( tr_session * s, tr_direction d, int Bps )
1317{
1318    assert( tr_isSession( s ) );
1319    assert( tr_isDirection( d ) );
1320    assert( Bps >= 0 );
1321
1322    s->speedLimit_Bps[d] = Bps;
1323
1324    updateBandwidth( s, d );
1325}
1326void
1327tr_sessionSetSpeedLimit_KBps( tr_session * s, tr_direction d, int KBps )
1328{
1329    tr_sessionSetSpeedLimit_Bps( s, d, toSpeedBytes( KBps ) );
1330}
1331
1332int
1333tr_sessionGetSpeedLimit_Bps( const tr_session * s, tr_direction d )
1334{
1335    assert( tr_isSession( s ) );
1336    assert( tr_isDirection( d ) );
1337
1338    return s->speedLimit_Bps[d];
1339}
1340int
1341tr_sessionGetSpeedLimit_KBps( const tr_session * s, tr_direction d )
1342{
1343    return toSpeedKBps( tr_sessionGetSpeedLimit_Bps( s, d ) );
1344}
1345
1346void
1347tr_sessionLimitSpeed( tr_session * s, tr_direction d, tr_bool b )
1348{
1349    assert( tr_isSession( s ) );
1350    assert( tr_isDirection( d ) );
1351    assert( tr_isBool( b ) );
1352
1353    s->speedLimitEnabled[d] = b;
1354
1355    updateBandwidth( s, d );
1356}
1357
1358tr_bool
1359tr_sessionIsSpeedLimited( const tr_session * s, tr_direction d )
1360{
1361    assert( tr_isSession( s ) );
1362    assert( tr_isDirection( d ) );
1363
1364    return s->speedLimitEnabled[d];
1365}
1366
1367/***
1368****  Alternative speed limits that are used during scheduled times
1369***/
1370
1371void
1372tr_sessionSetAltSpeed_Bps( tr_session * s, tr_direction d, int Bps )
1373{
1374    assert( tr_isSession( s ) );
1375    assert( tr_isDirection( d ) );
1376    assert( Bps >= 0 );
1377
1378    s->turtle.speedLimit_Bps[d] = Bps;
1379
1380    updateBandwidth( s, d );
1381}
1382
1383void
1384tr_sessionSetAltSpeed_KBps( tr_session * s, tr_direction d, int KBps )
1385{
1386    tr_sessionSetAltSpeed_Bps( s, d, toSpeedBytes( KBps ) );
1387}
1388
1389int
1390tr_sessionGetAltSpeed_Bps( const tr_session * s, tr_direction d )
1391{
1392    assert( tr_isSession( s ) );
1393    assert( tr_isDirection( d ) );
1394
1395    return s->turtle.speedLimit_Bps[d];
1396}
1397int
1398tr_sessionGetAltSpeed_KBps( const tr_session * s, tr_direction d )
1399{
1400    return toSpeedKBps( tr_sessionGetAltSpeed_Bps( s, d ) );
1401}
1402
1403static void
1404userPokedTheClock( tr_session * s, struct tr_turtle_info * t )
1405{
1406    tr_dbg( "Refreshing the turtle mode clock due to user changes" );
1407
1408    turtleUpdateTable( t );
1409
1410    if( t->isClockEnabled )
1411    {
1412        tr_bool enabled, changed;
1413        testTurtleTime( t, &enabled, &changed );
1414        useAltSpeed( s, t, enabled, TRUE );
1415    }
1416}
1417
1418void
1419tr_sessionUseAltSpeedTime( tr_session * s, tr_bool b )
1420{
1421    struct tr_turtle_info * t = &s->turtle;
1422
1423    assert( tr_isSession( s ) );
1424    assert( tr_isBool ( b ) );
1425
1426    if( t->isClockEnabled != b ) {
1427        t->isClockEnabled = b;
1428        userPokedTheClock( s, t );
1429    }
1430}
1431
1432tr_bool
1433tr_sessionUsesAltSpeedTime( const tr_session * s )
1434{
1435    assert( tr_isSession( s ) );
1436
1437    return s->turtle.isClockEnabled;
1438}
1439
1440void
1441tr_sessionSetAltSpeedBegin( tr_session * s, int minute )
1442{
1443    assert( tr_isSession( s ) );
1444    assert( 0<=minute && minute<(60*24) );
1445
1446    if( s->turtle.beginMinute != minute ) {
1447        s->turtle.beginMinute = minute;
1448        userPokedTheClock( s, &s->turtle );
1449    }
1450}
1451
1452int
1453tr_sessionGetAltSpeedBegin( const tr_session * s )
1454{
1455    assert( tr_isSession( s ) );
1456
1457    return s->turtle.beginMinute;
1458}
1459
1460void
1461tr_sessionSetAltSpeedEnd( tr_session * s, int minute )
1462{
1463    assert( tr_isSession( s ) );
1464    assert( 0<=minute && minute<(60*24) );
1465
1466    if( s->turtle.endMinute != minute ) {
1467        s->turtle.endMinute = minute;
1468        userPokedTheClock( s, &s->turtle );
1469    }
1470}
1471
1472int
1473tr_sessionGetAltSpeedEnd( const tr_session * s )
1474{
1475    assert( tr_isSession( s ) );
1476
1477    return s->turtle.endMinute;
1478}
1479
1480void
1481tr_sessionSetAltSpeedDay( tr_session * s, tr_sched_day days )
1482{
1483    assert( tr_isSession( s ) );
1484
1485    if( s->turtle.days != days ) {
1486        s->turtle.days = days;
1487        userPokedTheClock( s, &s->turtle );
1488    }
1489}
1490
1491tr_sched_day
1492tr_sessionGetAltSpeedDay( const tr_session * s )
1493{
1494    assert( tr_isSession( s ) );
1495
1496    return s->turtle.days;
1497}
1498
1499void
1500tr_sessionUseAltSpeed( tr_session * session, tr_bool enabled )
1501{
1502    useAltSpeed( session, &session->turtle, enabled, TRUE );
1503}
1504
1505tr_bool
1506tr_sessionUsesAltSpeed( const tr_session * s )
1507{
1508    assert( tr_isSession( s ) );
1509
1510    return s->turtle.isEnabled;
1511}
1512
1513void
1514tr_sessionSetAltSpeedFunc( tr_session       * session,
1515                           tr_altSpeedFunc    func,
1516                           void             * userData )
1517{
1518    assert( tr_isSession( session ) );
1519
1520    session->turtle.callback = func;
1521    session->turtle.callbackUserData = userData;
1522}
1523
1524void
1525tr_sessionClearAltSpeedFunc( tr_session * session )
1526{
1527    tr_sessionSetAltSpeedFunc( session, NULL, NULL );
1528}
1529
1530/***
1531****
1532***/
1533
1534void
1535tr_sessionSetPeerLimit( tr_session * session, uint16_t maxGlobalPeers )
1536{
1537    assert( tr_isSession( session ) );
1538
1539    tr_fdSetPeerLimit( session, maxGlobalPeers );
1540}
1541
1542uint16_t
1543tr_sessionGetPeerLimit( const tr_session * session )
1544{
1545    assert( tr_isSession( session ) );
1546
1547    return tr_fdGetPeerLimit( session );
1548}
1549
1550void
1551tr_sessionSetPeerLimitPerTorrent( tr_session  * session, uint16_t n )
1552{
1553    assert( tr_isSession( session ) );
1554
1555    session->peerLimitPerTorrent = n;
1556}
1557
1558uint16_t
1559tr_sessionGetPeerLimitPerTorrent( const tr_session * session )
1560{
1561    assert( tr_isSession( session ) );
1562
1563    return session->peerLimitPerTorrent;
1564}
1565
1566/***
1567****
1568***/
1569
1570void
1571tr_sessionSetPaused( tr_session * session, tr_bool isPaused )
1572{
1573    assert( tr_isSession( session ) );
1574
1575    session->pauseAddedTorrent = isPaused;
1576}
1577
1578tr_bool
1579tr_sessionGetPaused( const tr_session * session )
1580{
1581    assert( tr_isSession( session ) );
1582
1583    return session->pauseAddedTorrent;
1584}
1585
1586void
1587tr_sessionSetDeleteSource( tr_session * session, tr_bool deleteSource )
1588{
1589    assert( tr_isSession( session ) );
1590
1591    session->deleteSourceTorrent = deleteSource;
1592}
1593
1594tr_bool
1595tr_sessionGetDeleteSource( const tr_session * session )
1596{
1597    assert( tr_isSession( session ) );
1598
1599    return session->deleteSourceTorrent;
1600}
1601
1602/***
1603****
1604***/
1605
1606int
1607tr_sessionGetPieceSpeed_Bps( const tr_session * session, tr_direction dir )
1608{
1609    return tr_isSession( session ) ? tr_bandwidthGetPieceSpeed_Bps( session->bandwidth, 0, dir ) : 0;
1610}
1611double
1612tr_sessionGetPieceSpeed_KBps( const tr_session * session, tr_direction dir )
1613{
1614    return toSpeedKBps( tr_sessionGetPieceSpeed_Bps( session, dir ) );
1615}
1616
1617int
1618tr_sessionGetRawSpeed_Bps( const tr_session * session, tr_direction dir )
1619{
1620    return tr_isSession( session ) ? tr_bandwidthGetRawSpeed_Bps( session->bandwidth, 0, dir ) : 0;
1621}
1622double
1623tr_sessionGetRawSpeed_KBps( const tr_session * session, tr_direction dir )
1624{
1625    return toSpeedKBps( tr_sessionGetRawSpeed_Bps( session, dir ) );
1626}
1627
1628
1629int
1630tr_sessionCountTorrents( const tr_session * session )
1631{
1632    return tr_isSession( session ) ? session->torrentCount : 0;
1633}
1634
1635static int
1636compareTorrentByCur( const void * va, const void * vb )
1637{
1638    const tr_torrent * a = *(const tr_torrent**)va;
1639    const tr_torrent * b = *(const tr_torrent**)vb;
1640    const uint64_t     aCur = a->downloadedCur + a->uploadedCur;
1641    const uint64_t     bCur = b->downloadedCur + b->uploadedCur;
1642
1643    if( aCur != bCur )
1644        return aCur > bCur ? -1 : 1; /* close the biggest torrents first */
1645
1646    return 0;
1647}
1648
1649static void closeBlocklists( tr_session * );
1650
1651static void
1652sessionCloseImpl( void * vsession )
1653{
1654    tr_session *  session = vsession;
1655    tr_torrent *  tor;
1656    int           i, n;
1657    tr_torrent ** torrents;
1658
1659    assert( tr_isSession( session ) );
1660
1661    free_incoming_peer_port( session );
1662
1663    if( session->isLPDEnabled )
1664        tr_lpdUninit( session );
1665
1666    if( session->isDHTEnabled )
1667        tr_dhtUninit( session );
1668
1669    evtimer_del( session->saveTimer );
1670    tr_free( session->saveTimer );
1671    session->saveTimer = NULL;
1672
1673    evtimer_del( session->nowTimer );
1674    tr_free( session->nowTimer );
1675    session->nowTimer = NULL;
1676
1677    tr_verifyClose( session );
1678    tr_sharedClose( session );
1679    tr_rpcClose( &session->rpcServer );
1680
1681    /* close the torrents.  get the most active ones first so that
1682     * if we can't get them all closed in a reasonable amount of time,
1683     * at least we get the most important ones first. */
1684    tor = NULL;
1685    n = session->torrentCount;
1686    torrents = tr_new( tr_torrent *, session->torrentCount );
1687    for( i = 0; i < n; ++i )
1688        torrents[i] = tor = tr_torrentNext( session, tor );
1689    qsort( torrents, n, sizeof( tr_torrent* ), compareTorrentByCur );
1690    for( i = 0; i < n; ++i )
1691        tr_torrentFree( torrents[i] );
1692    tr_free( torrents );
1693
1694    tr_cacheFree( session->cache );
1695    session->cache = NULL;
1696    tr_announcerClose( session );
1697    tr_statsClose( session );
1698    tr_peerMgrFree( session->peerMgr );
1699    tr_webClose( session, TR_WEB_CLOSE_WHEN_IDLE );
1700
1701    closeBlocklists( session );
1702
1703    tr_fdClose( session );
1704
1705    session->isClosed = TRUE;
1706}
1707
1708static int
1709deadlineReached( const time_t deadline )
1710{
1711    return time( NULL ) >= deadline;
1712}
1713
1714#define SHUTDOWN_MAX_SECONDS 20
1715
1716void
1717tr_sessionClose( tr_session * session )
1718{
1719    const time_t deadline = time( NULL ) + SHUTDOWN_MAX_SECONDS;
1720
1721    assert( tr_isSession( session ) );
1722
1723    dbgmsg( "shutting down transmission session %p... now is %zu, deadline is %zu", session, (size_t)time(NULL), (size_t)deadline );
1724
1725    /* close the session */
1726    tr_runInEventThread( session, sessionCloseImpl, session );
1727    while( !session->isClosed && !deadlineReached( deadline ) )
1728    {
1729        dbgmsg( "waiting for the libtransmission thread to finish" );
1730        tr_wait_msec( 100 );
1731    }
1732
1733    /* "shared" and "tracker" have live sockets,
1734     * so we need to keep the transmission thread alive
1735     * for a bit while they tell the router & tracker
1736     * that we're closing now */
1737    while( ( session->shared || session->web || session->announcer )
1738           && !deadlineReached( deadline ) )
1739    {
1740        dbgmsg( "waiting on port unmap (%p) or announcer (%p)... now %zu deadline %zu",
1741                session->shared, session->announcer, (size_t)time(NULL), (size_t)deadline );
1742        tr_wait_msec( 100 );
1743    }
1744
1745    tr_webClose( session, TR_WEB_CLOSE_NOW );
1746
1747    /* close the libtransmission thread */
1748    tr_eventClose( session );
1749    while( session->events != NULL )
1750    {
1751        static tr_bool forced = FALSE;
1752        dbgmsg( "waiting for libtransmission thread to finish... now %zu deadline %zu", (size_t)time(NULL), (size_t)deadline );
1753        tr_wait_msec( 500 );
1754        if( deadlineReached( deadline ) && !forced )
1755        {
1756            dbgmsg( "calling event_loopbreak()" );
1757            forced = TRUE;
1758            event_loopbreak( );
1759        }
1760        if( deadlineReached( deadline+3 ) )
1761        {
1762            dbgmsg( "deadline+3 reached... calling break...\n" );
1763            break;
1764        }
1765    }
1766
1767    /* free the session memory */
1768    tr_bencFree( &session->removedTorrents );
1769    tr_bandwidthFree( session->bandwidth );
1770    tr_bitfieldDestruct( &session->turtle.minutes );
1771    tr_lockFree( session->lock );
1772    if( session->metainfoLookup ) {
1773        tr_bencFree( session->metainfoLookup );
1774        tr_free( session->metainfoLookup );
1775    }
1776    tr_free( session->torrentDoneScript );
1777    tr_free( session->buffer );
1778    tr_free( session->tag );
1779    tr_free( session->configDir );
1780    tr_free( session->resumeDir );
1781    tr_free( session->torrentDir );
1782    tr_free( session->downloadDir );
1783    tr_free( session->incompleteDir );
1784    tr_free( session->blocklist_url );
1785    tr_free( session->peer_congestion_algorithm );
1786    tr_free( session );
1787}
1788
1789tr_torrent **
1790tr_sessionLoadTorrents( tr_session * session,
1791                        tr_ctor    * ctor,
1792                        int        * setmeCount )
1793{
1794    int           i, n = 0;
1795    struct stat   sb;
1796    DIR *         odir = NULL;
1797    const char *  dirname = tr_getTorrentDir( session );
1798    tr_torrent ** torrents;
1799    tr_list *     l = NULL, *list = NULL;
1800
1801    assert( tr_isSession( session ) );
1802
1803    tr_ctorSetSave( ctor, FALSE ); /* since we already have them */
1804
1805    if( !stat( dirname, &sb )
1806      && S_ISDIR( sb.st_mode )
1807      && ( ( odir = opendir ( dirname ) ) ) )
1808    {
1809        struct dirent *d;
1810        for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
1811        {
1812            if( tr_str_has_suffix( d->d_name, ".torrent" ) )
1813            {
1814                tr_torrent * tor;
1815                char * path = tr_buildPath( dirname, d->d_name, NULL );
1816                tr_ctorSetMetainfoFromFile( ctor, path );
1817                if(( tor = tr_torrentNew( ctor, NULL )))
1818                {
1819                    tr_list_append( &list, tor );
1820                    ++n;
1821                }
1822                tr_free( path );
1823            }
1824        }
1825        closedir( odir );
1826    }
1827
1828    torrents = tr_new( tr_torrent *, n );
1829    for( i = 0, l = list; l != NULL; l = l->next )
1830        torrents[i++] = (tr_torrent*) l->data;
1831    assert( i == n );
1832
1833    tr_list_free( &list, NULL );
1834
1835    if( n )
1836        tr_inf( _( "Loaded %d torrents" ), n );
1837
1838    if( setmeCount )
1839        *setmeCount = n;
1840    return torrents;
1841}
1842
1843/***
1844****
1845***/
1846
1847void
1848tr_sessionSetPexEnabled( tr_session * session,
1849                         tr_bool      enabled )
1850{
1851    assert( tr_isSession( session ) );
1852
1853    session->isPexEnabled = enabled != 0;
1854}
1855
1856tr_bool
1857tr_sessionIsPexEnabled( const tr_session * session )
1858{
1859    assert( tr_isSession( session ) );
1860
1861    return session->isPexEnabled;
1862}
1863
1864tr_bool
1865tr_sessionAllowsDHT( const tr_session * session UNUSED )
1866{
1867    return tr_sessionIsDHTEnabled( session );
1868}
1869
1870tr_bool
1871tr_sessionIsDHTEnabled( const tr_session * session )
1872{
1873    assert( tr_isSession( session ) );
1874
1875    return session->isDHTEnabled;
1876}
1877
1878static void
1879toggleDHTImpl(  void * data )
1880{
1881    tr_session * session = data;
1882    assert( tr_isSession( session ) );
1883
1884    if( session->isDHTEnabled )
1885        tr_dhtUninit( session );
1886
1887    session->isDHTEnabled = !session->isDHTEnabled;
1888
1889    if( session->isDHTEnabled )
1890        tr_dhtInit( session, &session->public_ipv4->addr );
1891}
1892
1893void
1894tr_sessionSetDHTEnabled( tr_session * session, tr_bool enabled )
1895{
1896    assert( tr_isSession( session ) );
1897    assert( tr_isBool( enabled ) );
1898
1899    if( ( enabled != 0 ) != ( session->isDHTEnabled != 0 ) )
1900        tr_runInEventThread( session, toggleDHTImpl, session );
1901}
1902
1903static void
1904toggleLPDImpl(  void * data )
1905{
1906    tr_session * session = data;
1907    assert( tr_isSession( session ) );
1908
1909    if( session->isLPDEnabled )
1910        tr_lpdUninit( session );
1911
1912    session->isLPDEnabled = !session->isLPDEnabled;
1913
1914    if( session->isLPDEnabled )
1915        tr_lpdInit( session, &session->public_ipv4->addr );
1916}
1917
1918void
1919tr_sessionSetLPDEnabled( tr_session * session, tr_bool enabled )
1920{
1921    assert( tr_isSession( session ) );
1922    assert( tr_isBool( enabled ) );
1923
1924    if( ( enabled != 0 ) != ( session->isLPDEnabled != 0 ) )
1925        tr_runInEventThread( session, toggleLPDImpl, session );
1926}
1927
1928tr_bool
1929tr_sessionIsLPDEnabled( const tr_session * session )
1930{
1931    assert( tr_isSession( session ) );
1932
1933    return session->isLPDEnabled;
1934}
1935
1936tr_bool
1937tr_sessionAllowsLPD( const tr_session * session )
1938{
1939    return tr_sessionIsLPDEnabled( session );
1940}
1941
1942/***
1943****
1944***/
1945
1946void
1947tr_sessionSetCacheLimit_MB( tr_session * session, int max_bytes )
1948{
1949    assert( tr_isSession( session ) );
1950
1951    tr_cacheSetLimit( session->cache, toMemBytes( max_bytes ) );
1952}
1953
1954int
1955tr_sessionGetCacheLimit_MB( const tr_session * session )
1956{
1957    assert( tr_isSession( session ) );
1958
1959    return toMemMB( tr_cacheGetLimit( session->cache ) );
1960}
1961
1962/***
1963****
1964***/
1965
1966void
1967tr_sessionSetLazyBitfieldEnabled( tr_session * session,
1968                                  tr_bool      enabled )
1969{
1970    assert( tr_isSession( session ) );
1971
1972    session->useLazyBitfield = enabled != 0;
1973}
1974
1975tr_bool
1976tr_sessionIsLazyBitfieldEnabled( const tr_session * session )
1977{
1978    assert( tr_isSession( session ) );
1979
1980    return session->useLazyBitfield;
1981}
1982
1983/***
1984****
1985***/
1986
1987struct port_forwarding_data
1988{
1989    tr_bool enabled;
1990    struct tr_shared * shared;
1991};
1992
1993static void
1994setPortForwardingEnabled( void * vdata )
1995{
1996    struct port_forwarding_data * data = vdata;
1997    tr_sharedTraversalEnable( data->shared, data->enabled );
1998    tr_free( data );
1999}
2000
2001void
2002tr_sessionSetPortForwardingEnabled( tr_session  * session, tr_bool enabled )
2003{
2004    struct port_forwarding_data * d;
2005    d = tr_new0( struct port_forwarding_data, 1 );
2006    d->shared = session->shared;
2007    d->enabled = enabled;
2008    tr_runInEventThread( session, setPortForwardingEnabled, d );
2009}
2010
2011tr_bool
2012tr_sessionIsPortForwardingEnabled( const tr_session * session )
2013{
2014    assert( tr_isSession( session ) );
2015
2016    return tr_sharedTraversalIsEnabled( session->shared );
2017}
2018
2019/***
2020****
2021***/
2022
2023static int
2024tr_stringEndsWith( const char * str, const char * end )
2025{
2026    const size_t slen = strlen( str );
2027    const size_t elen = strlen( end );
2028
2029    return slen >= elen && !memcmp( &str[slen - elen], end, elen );
2030}
2031
2032static void
2033loadBlocklists( tr_session * session )
2034{
2035    int         binCount = 0;
2036    int         newCount = 0;
2037    struct stat sb;
2038    char      * dirname;
2039    DIR *       odir = NULL;
2040    tr_list *   list = NULL;
2041    const tr_bool   isEnabled = session->isBlocklistEnabled;
2042
2043    /* walk through the directory and find blocklists */
2044    dirname = tr_buildPath( session->configDir, "blocklists", NULL );
2045    if( !stat( dirname,
2046               &sb ) && S_ISDIR( sb.st_mode )
2047      && ( ( odir = opendir( dirname ) ) ) )
2048    {
2049        struct dirent *d;
2050        for( d = readdir( odir ); d; d = readdir( odir ) )
2051        {
2052            char * filename;
2053
2054            if( !d->d_name || d->d_name[0] == '.' ) /* skip dotfiles, ., and ..
2055                                                      */
2056                continue;
2057
2058            filename = tr_buildPath( dirname, d->d_name, NULL );
2059
2060            if( tr_stringEndsWith( filename, ".bin" ) )
2061            {
2062                /* if we don't already have this blocklist, add it */
2063                if( !tr_list_find( list, filename,
2064                                   (TrListCompareFunc)strcmp ) )
2065                {
2066                    tr_list_append( &list,
2067                                   _tr_blocklistNew( filename, isEnabled ) );
2068                    ++binCount;
2069                }
2070            }
2071            else
2072            {
2073                /* strip out the file suffix, if there is one, and add ".bin"
2074                  instead */
2075                tr_blocklist * b;
2076                const char *   dot = strrchr( d->d_name, '.' );
2077                const int      len = dot ? dot - d->d_name
2078                                         : (int)strlen( d->d_name );
2079                char         * tmp = tr_strdup_printf(
2080                                        "%s" TR_PATH_DELIMITER_STR "%*.*s.bin",
2081                                        dirname, len, len, d->d_name );
2082                b = _tr_blocklistNew( tmp, isEnabled );
2083                _tr_blocklistSetContent( b, filename );
2084                tr_list_append( &list, b );
2085                ++newCount;
2086                tr_free( tmp );
2087            }
2088
2089            tr_free( filename );
2090        }
2091
2092        closedir( odir );
2093    }
2094
2095    session->blocklists = list;
2096
2097    if( binCount )
2098        tr_dbg( "Found %d blocklists in \"%s\"", binCount, dirname );
2099    if( newCount )
2100        tr_dbg( "Found %d new blocklists in \"%s\"", newCount, dirname );
2101
2102    tr_free( dirname );
2103}
2104
2105static void
2106closeBlocklists( tr_session * session )
2107{
2108    tr_list_free( &session->blocklists,
2109                  (TrListForeachFunc)_tr_blocklistFree );
2110}
2111
2112void
2113tr_sessionReloadBlocklists( tr_session * session )
2114{
2115    closeBlocklists( session );
2116    loadBlocklists( session );
2117
2118    tr_peerMgrOnBlocklistChanged( session->peerMgr );
2119}
2120
2121int
2122tr_blocklistGetRuleCount( const tr_session * session )
2123{
2124    int       n = 0;
2125    tr_list * l;
2126
2127    assert( tr_isSession( session ) );
2128
2129    for( l = session->blocklists; l; l = l->next )
2130        n += _tr_blocklistGetRuleCount( l->data );
2131    return n;
2132}
2133
2134tr_bool
2135tr_blocklistIsEnabled( const tr_session * session )
2136{
2137    assert( tr_isSession( session ) );
2138
2139    return session->isBlocklistEnabled;
2140}
2141
2142void
2143tr_blocklistSetEnabled( tr_session * session,
2144                        tr_bool      isEnabled )
2145{
2146    tr_list * l;
2147
2148    assert( tr_isSession( session ) );
2149
2150    session->isBlocklistEnabled = isEnabled != 0;
2151
2152    for( l=session->blocklists; l!=NULL; l=l->next )
2153        _tr_blocklistSetEnabled( l->data, isEnabled );
2154}
2155
2156tr_bool
2157tr_blocklistExists( const tr_session * session )
2158{
2159    assert( tr_isSession( session ) );
2160
2161    return session->blocklists != NULL;
2162}
2163
2164int
2165tr_blocklistSetContent( tr_session * session, const char * contentFilename )
2166{
2167    tr_list * l;
2168    int ruleCount;
2169    tr_blocklist * b;
2170    const char * defaultName = DEFAULT_BLOCKLIST_FILENAME;
2171    tr_sessionLock( session );
2172
2173    for( b = NULL, l = session->blocklists; !b && l; l = l->next )
2174        if( tr_stringEndsWith( _tr_blocklistGetFilename( l->data ),
2175                               defaultName ) )
2176            b = l->data;
2177
2178    if( !b )
2179    {
2180        char * path = tr_buildPath( session->configDir, "blocklists", defaultName, NULL );
2181        b = _tr_blocklistNew( path, session->isBlocklistEnabled );
2182        tr_list_append( &session->blocklists, b );
2183        tr_free( path );
2184    }
2185
2186    ruleCount = _tr_blocklistSetContent( b, contentFilename );
2187    tr_sessionUnlock( session );
2188    return ruleCount;
2189}
2190
2191tr_bool
2192tr_sessionIsAddressBlocked( const tr_session * session,
2193                            const tr_address * addr )
2194{
2195    tr_list * l;
2196
2197    assert( tr_isSession( session ) );
2198
2199    for( l = session->blocklists; l; l = l->next )
2200        if( _tr_blocklistHasAddress( l->data, addr ) )
2201            return TRUE;
2202    return FALSE;
2203}
2204
2205void
2206tr_blocklistSetURL( tr_session * session, const char * url )
2207{
2208    if( session->blocklist_url != url )
2209    {
2210        tr_free( session->blocklist_url );
2211        session->blocklist_url = tr_strdup( url );
2212    }
2213}
2214
2215const char *
2216tr_blocklistGetURL ( const tr_session * session )
2217{
2218    return session->blocklist_url;
2219}
2220
2221
2222/***
2223****
2224***/
2225
2226static void
2227metainfoLookupInit( tr_session * session )
2228{
2229    struct stat  sb;
2230    const char * dirname = tr_getTorrentDir( session );
2231    DIR *        odir = NULL;
2232    tr_ctor *    ctor = NULL;
2233    tr_benc * lookup;
2234    int n = 0;
2235
2236    assert( tr_isSession( session ) );
2237
2238    /* walk through the directory and find the mappings */
2239    lookup = tr_new0( tr_benc, 1 );
2240    tr_bencInitDict( lookup, 0 );
2241    ctor = tr_ctorNew( session );
2242    tr_ctorSetSave( ctor, FALSE ); /* since we already have them */
2243    if( !stat( dirname, &sb ) && S_ISDIR( sb.st_mode ) && ( ( odir = opendir( dirname ) ) ) )
2244    {
2245        struct dirent *d;
2246        while(( d = readdir( odir )))
2247        {
2248            if( tr_str_has_suffix( d->d_name, ".torrent" ) )
2249            {
2250                tr_info inf;
2251                char * path = tr_buildPath( dirname, d->d_name, NULL );
2252                tr_ctorSetMetainfoFromFile( ctor, path );
2253                if( !tr_torrentParse( ctor, &inf ) )
2254                {
2255                    ++n;
2256                    tr_bencDictAddStr( lookup, inf.hashString, path );
2257                }
2258                tr_free( path );
2259            }
2260        }
2261        closedir( odir );
2262    }
2263    tr_ctorFree( ctor );
2264
2265    session->metainfoLookup = lookup;
2266    tr_dbg( "Found %d torrents in \"%s\"", n, dirname );
2267}
2268
2269const char*
2270tr_sessionFindTorrentFile( const tr_session * session,
2271                           const char       * hashString )
2272{
2273    const char * filename = NULL;
2274    if( !session->metainfoLookup )
2275        metainfoLookupInit( (tr_session*)session );
2276    tr_bencDictFindStr( session->metainfoLookup, hashString, &filename );
2277    return filename;
2278}
2279
2280void
2281tr_sessionSetTorrentFile( tr_session * session,
2282                          const char * hashString,
2283                          const char * filename )
2284{
2285    /* since we walk session->configDir/torrents/ to build the lookup table,
2286     * and tr_sessionSetTorrentFile() is just to tell us there's a new file
2287     * in that same directory, we don't need to do anything here if the
2288     * lookup table hasn't been built yet */
2289    if( session->metainfoLookup )
2290        tr_bencDictAddStr( session->metainfoLookup, hashString, filename );
2291}
2292
2293tr_torrent*
2294tr_torrentNext( tr_session * session,
2295                tr_torrent * tor )
2296{
2297    tr_torrent * ret;
2298
2299    assert( !session || tr_isSession( session ) );
2300
2301    if( !session )
2302        ret = NULL;
2303    else if( !tor )
2304        ret = session->torrentList;
2305    else
2306        ret = tor->next;
2307
2308    return ret;
2309}
2310
2311/***
2312****
2313***/
2314
2315void
2316tr_sessionSetRPCEnabled( tr_session * session,
2317                         tr_bool      isEnabled )
2318{
2319    assert( tr_isSession( session ) );
2320
2321    tr_rpcSetEnabled( session->rpcServer, isEnabled );
2322}
2323
2324tr_bool
2325tr_sessionIsRPCEnabled( const tr_session * session )
2326{
2327    assert( tr_isSession( session ) );
2328
2329    return tr_rpcIsEnabled( session->rpcServer );
2330}
2331
2332void
2333tr_sessionSetRPCPort( tr_session * session,
2334                      tr_port      port )
2335{
2336    assert( tr_isSession( session ) );
2337
2338    tr_rpcSetPort( session->rpcServer, port );
2339}
2340
2341tr_port
2342tr_sessionGetRPCPort( const tr_session * session )
2343{
2344    assert( tr_isSession( session ) );
2345
2346    return tr_rpcGetPort( session->rpcServer );
2347}
2348
2349void
2350tr_sessionSetRPCUrl( tr_session * session,
2351                     const char * url )
2352{
2353    assert( tr_isSession( session ) );
2354
2355    tr_rpcSetUrl( session->rpcServer, url );
2356}
2357
2358const char*
2359tr_sessionGetRPCUrl( const tr_session * session )
2360{
2361    assert( tr_isSession( session ) );
2362
2363    return tr_rpcGetUrl( session->rpcServer );
2364}
2365
2366void
2367tr_sessionSetRPCCallback( tr_session * session,
2368                          tr_rpc_func  func,
2369                          void *       user_data )
2370{
2371    assert( tr_isSession( session ) );
2372
2373    session->rpc_func = func;
2374    session->rpc_func_user_data = user_data;
2375}
2376
2377void
2378tr_sessionSetRPCWhitelist( tr_session * session,
2379                           const char * whitelist )
2380{
2381    assert( tr_isSession( session ) );
2382
2383    tr_rpcSetWhitelist( session->rpcServer, whitelist );
2384}
2385
2386const char*
2387tr_sessionGetRPCWhitelist( const tr_session * session )
2388{
2389    assert( tr_isSession( session ) );
2390
2391    return tr_rpcGetWhitelist( session->rpcServer );
2392}
2393
2394void
2395tr_sessionSetRPCWhitelistEnabled( tr_session * session,
2396                                  tr_bool      isEnabled )
2397{
2398    assert( tr_isSession( session ) );
2399
2400    tr_rpcSetWhitelistEnabled( session->rpcServer, isEnabled );
2401}
2402
2403tr_bool
2404tr_sessionGetRPCWhitelistEnabled( const tr_session * session )
2405{
2406    assert( tr_isSession( session ) );
2407
2408    return tr_rpcGetWhitelistEnabled( session->rpcServer );
2409}
2410
2411
2412void
2413tr_sessionSetRPCPassword( tr_session * session,
2414                          const char * password )
2415{
2416    assert( tr_isSession( session ) );
2417
2418    tr_rpcSetPassword( session->rpcServer, password );
2419}
2420
2421const char*
2422tr_sessionGetRPCPassword( const tr_session * session )
2423{
2424    assert( tr_isSession( session ) );
2425
2426    return tr_rpcGetPassword( session->rpcServer );
2427}
2428
2429void
2430tr_sessionSetRPCUsername( tr_session * session,
2431                          const char * username )
2432{
2433    assert( tr_isSession( session ) );
2434
2435    tr_rpcSetUsername( session->rpcServer, username );
2436}
2437
2438const char*
2439tr_sessionGetRPCUsername( const tr_session * session )
2440{
2441    assert( tr_isSession( session ) );
2442
2443    return tr_rpcGetUsername( session->rpcServer );
2444}
2445
2446void
2447tr_sessionSetRPCPasswordEnabled( tr_session * session,
2448                                 tr_bool      isEnabled )
2449{
2450    assert( tr_isSession( session ) );
2451
2452    tr_rpcSetPasswordEnabled( session->rpcServer, isEnabled );
2453}
2454
2455tr_bool
2456tr_sessionIsRPCPasswordEnabled( const tr_session * session )
2457{
2458    assert( tr_isSession( session ) );
2459
2460    return tr_rpcIsPasswordEnabled( session->rpcServer );
2461}
2462
2463const char *
2464tr_sessionGetRPCBindAddress( const tr_session * session )
2465{
2466    assert( tr_isSession( session ) );
2467
2468    return tr_rpcGetBindAddress( session->rpcServer );
2469}
2470
2471/****
2472*****
2473****/
2474
2475tr_bool
2476tr_sessionIsTorrentDoneScriptEnabled( const tr_session * session )
2477{
2478    assert( tr_isSession( session ) );
2479
2480    return session->isTorrentDoneScriptEnabled;
2481}
2482
2483void
2484tr_sessionSetTorrentDoneScriptEnabled( tr_session * session, tr_bool isEnabled )
2485{
2486    assert( tr_isSession( session ) );
2487    assert( tr_isBool( isEnabled ) );
2488
2489    session->isTorrentDoneScriptEnabled = isEnabled;
2490}
2491
2492const char *
2493tr_sessionGetTorrentDoneScript( const tr_session * session )
2494{
2495    assert( tr_isSession( session ) );
2496
2497    return session->torrentDoneScript;
2498}
2499
2500void
2501tr_sessionSetTorrentDoneScript( tr_session * session, const char * scriptFilename )
2502{
2503    assert( tr_isSession( session ) );
2504
2505    if( session->torrentDoneScript != scriptFilename )
2506    {
2507        tr_free( session->torrentDoneScript );
2508        session->torrentDoneScript = tr_strdup( scriptFilename );
2509    }
2510}
2511
2512/***
2513****
2514***/
2515
2516void
2517tr_sessionSetWebConfigFunc( tr_session * session, void (*func)(tr_session*, void*, const char* ) )
2518{
2519    session->curl_easy_config_func = func;
2520}
Note: See TracBrowser for help on using the repository browser.