source: trunk/libtransmission/session.c @ 11496

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

#3677 (libT) "Save .resume files less frequently to avoid excessive disk IO" -- fixed.

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