source: trunk/libtransmission/session.c @ 11599

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

(trunk) Join the 21st century and use only 1 space at the end sentences. This commit is nearly as important as the semi-annual ones that remove trailing spaces from the ends of lines of code... :)

  • 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 11599 2010-12-27 19:18:17Z 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 <event2/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_free( b->ev );
156        b->ev = NULL;
157        tr_netCloseSocket( b->socket );
158    }
159}
160
161static void
162close_incoming_peer_port( tr_session * session )
163{
164    close_bindinfo( session->public_ipv4 );
165    close_bindinfo( session->public_ipv6 );
166}
167
168static void
169free_incoming_peer_port( tr_session * session )
170{
171    close_bindinfo( session->public_ipv4 );
172    tr_free( session->public_ipv4 );
173    session->public_ipv4 = NULL;
174
175    close_bindinfo( session->public_ipv6 );
176    tr_free( session->public_ipv6 );
177    session->public_ipv6 = NULL;
178}
179
180static void
181accept_incoming_peer( int fd, short what UNUSED, void * vsession )
182{
183    int clientSocket;
184    tr_port clientPort;
185    tr_address clientAddr;
186    tr_session * session = vsession;
187
188    clientSocket = tr_netAccept( session, fd, &clientAddr, &clientPort );
189    if( clientSocket > 0 ) {
190        tr_deepLog( __FILE__, __LINE__, NULL, "new incoming connection %d (%s)",
191                   clientSocket, tr_peerIoAddrStr( &clientAddr, clientPort ) );
192        tr_peerMgrAddIncoming( session->peerMgr, &clientAddr, clientPort, clientSocket );
193    }
194}
195
196static void
197open_incoming_peer_port( tr_session * session )
198{
199    struct tr_bindinfo * b;
200
201    /* bind an ipv4 port to listen for incoming peers... */
202    b = session->public_ipv4;
203    b->socket = tr_netBindTCP( &b->addr, session->private_peer_port, FALSE );
204    if( b->socket >= 0 ) {
205        b->ev = event_new( session->event_base, b->socket, EV_READ | EV_PERSIST, accept_incoming_peer, session );
206        event_add( b->ev, NULL );
207    }
208
209    /* and do the exact same thing for ipv6, if it's supported... */
210    if( tr_net_hasIPv6( session->private_peer_port ) ) {
211        b = session->public_ipv6;
212        b->socket = tr_netBindTCP( &b->addr, session->private_peer_port, FALSE );
213        if( b->socket >= 0 ) {
214            b->ev = event_new( session->event_base, b->socket, EV_READ | EV_PERSIST, accept_incoming_peer, session );
215            event_add( b->ev, NULL );
216        }
217    }
218}
219
220const tr_address*
221tr_sessionGetPublicAddress( const tr_session * session, int tr_af_type )
222{
223    const struct tr_bindinfo * bindinfo;
224
225    switch( tr_af_type )
226    {
227        case TR_AF_INET:  bindinfo = session->public_ipv4; break;
228        case TR_AF_INET6: bindinfo = session->public_ipv6; break;
229        default:          bindinfo = NULL;                 break;
230    }
231
232    return bindinfo ? &bindinfo->addr : NULL;
233}
234
235/***
236****
237***/
238
239#ifdef TR_EMBEDDED
240 #define TR_DEFAULT_ENCRYPTION   TR_CLEAR_PREFERRED
241#else
242 #define TR_DEFAULT_ENCRYPTION   TR_ENCRYPTION_PREFERRED
243#endif
244
245void
246tr_sessionGetDefaultSettings( const char * configDir UNUSED, tr_benc * d )
247{
248    assert( tr_bencIsDict( d ) );
249
250    tr_bencDictReserve( d, 60 );
251    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED,        FALSE );
252    tr_bencDictAddStr ( d, TR_PREFS_KEY_BLOCKLIST_URL,            "http://www.example.com/blocklist" );
253    tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB,        DEFAULT_CACHE_SIZE_MB );
254    tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED,              TRUE );
255    tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED,              FALSE );
256    tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR,             tr_getDefaultDownloadDir( ) );
257    tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps,              100 );
258    tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED,           FALSE );
259    tr_bencDictAddInt ( d, TR_PREFS_KEY_ENCRYPTION,               TR_DEFAULT_ENCRYPTION );
260    tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT,               30 );
261    tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED,       FALSE );
262    tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR,           tr_getDefaultDownloadDir( ) );
263    tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED,   FALSE );
264    tr_bencDictAddBool( d, TR_PREFS_KEY_LAZY_BITFIELD,            TRUE );
265    tr_bencDictAddInt ( d, TR_PREFS_KEY_MSGLEVEL,                 TR_MSG_INF );
266    tr_bencDictAddInt ( d, TR_PREFS_KEY_OPEN_FILE_LIMIT,          atoi( TR_DEFAULT_OPEN_FILE_LIMIT_STR ) );
267    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL,        atoi( TR_DEFAULT_PEER_LIMIT_GLOBAL_STR ) );
268    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT,       atoi( TR_DEFAULT_PEER_LIMIT_TORRENT_STR ) );
269    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT,                atoi( TR_DEFAULT_PEER_PORT_STR ) );
270    tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, FALSE );
271    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW,     49152 );
272    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH,    65535 );
273    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_SOCKET_TOS,          atoi( TR_DEFAULT_PEER_SOCKET_TOS_STR ) );
274    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED,              TRUE );
275    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING,          TRUE );
276    tr_bencDictAddInt ( d, TR_PREFS_KEY_PREALLOCATION,            TR_PREALLOCATE_SPARSE );
277    tr_bencDictAddReal( d, TR_PREFS_KEY_RATIO,                    2.0 );
278    tr_bencDictAddBool( d, TR_PREFS_KEY_RATIO_ENABLED,            FALSE );
279    tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES,     TRUE );
280    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_AUTH_REQUIRED,        FALSE );
281    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_BIND_ADDRESS,         "0.0.0.0" );
282    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_ENABLED,              FALSE );
283    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_PASSWORD,             "" );
284    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_USERNAME,             "" );
285    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_WHITELIST,            TR_DEFAULT_RPC_WHITELIST );
286    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_WHITELIST_ENABLED,    TRUE );
287    tr_bencDictAddInt ( d, TR_PREFS_KEY_RPC_PORT,                 atoi( TR_DEFAULT_RPC_PORT_STR ) );
288    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_URL,                  TR_DEFAULT_RPC_URL_STR );
289    tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, "" );
290    tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, FALSE );
291    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED,        FALSE );
292    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP_KBps,        50 ); /* half the regular */
293    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps,      50 ); /* half the regular */
294    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN,     540 ); /* 9am */
295    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED,   FALSE );
296    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,       1020 ); /* 5pm */
297    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,       TR_SCHED_ALL );
298    tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps,              100 );
299    tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED,           FALSE );
300    tr_bencDictAddInt ( d, TR_PREFS_KEY_UMASK,                    022 );
301    tr_bencDictAddInt ( d, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, 14 );
302    tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV4,        TR_DEFAULT_BIND_ADDRESS_IPV4 );
303    tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV6,        TR_DEFAULT_BIND_ADDRESS_IPV6 );
304    tr_bencDictAddBool( d, TR_PREFS_KEY_START,                    TRUE );
305    tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL,           FALSE );
306}
307
308void
309tr_sessionGetSettings( tr_session * s, struct tr_benc * d )
310{
311    assert( tr_bencIsDict( d ) );
312
313    tr_bencDictReserve( d, 60 );
314    tr_bencDictAddBool( d, TR_PREFS_KEY_BLOCKLIST_ENABLED,        tr_blocklistIsEnabled( s ) );
315    tr_bencDictAddStr ( d, TR_PREFS_KEY_BLOCKLIST_URL,            tr_blocklistGetURL( s ) );
316    tr_bencDictAddInt ( d, TR_PREFS_KEY_MAX_CACHE_SIZE_MB,        tr_sessionGetCacheLimit_MB( s ) );
317    tr_bencDictAddBool( d, TR_PREFS_KEY_DHT_ENABLED,              s->isDHTEnabled );
318    tr_bencDictAddBool( d, TR_PREFS_KEY_LPD_ENABLED,              s->isLPDEnabled );
319    tr_bencDictAddStr ( d, TR_PREFS_KEY_DOWNLOAD_DIR,             s->downloadDir );
320    tr_bencDictAddInt ( d, TR_PREFS_KEY_DSPEED_KBps,              tr_sessionGetSpeedLimit_KBps( s, TR_DOWN ) );
321    tr_bencDictAddBool( d, TR_PREFS_KEY_DSPEED_ENABLED,           tr_sessionIsSpeedLimited( s, TR_DOWN ) );
322    tr_bencDictAddInt ( d, TR_PREFS_KEY_ENCRYPTION,               s->encryptionMode );
323    tr_bencDictAddInt ( d, TR_PREFS_KEY_IDLE_LIMIT,               tr_sessionGetIdleLimit( s ) );
324    tr_bencDictAddBool( d, TR_PREFS_KEY_IDLE_LIMIT_ENABLED,       tr_sessionIsIdleLimited( s ) );
325    tr_bencDictAddStr ( d, TR_PREFS_KEY_INCOMPLETE_DIR,           tr_sessionGetIncompleteDir( s ) );
326    tr_bencDictAddBool( d, TR_PREFS_KEY_INCOMPLETE_DIR_ENABLED,   tr_sessionIsIncompleteDirEnabled( s ) );
327    tr_bencDictAddBool( d, TR_PREFS_KEY_LAZY_BITFIELD,            s->useLazyBitfield );
328    tr_bencDictAddInt ( d, TR_PREFS_KEY_MSGLEVEL,                 tr_getMessageLevel( ) );
329    tr_bencDictAddInt ( d, TR_PREFS_KEY_OPEN_FILE_LIMIT,          tr_fdGetFileLimit( s ) );
330    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_GLOBAL,        tr_sessionGetPeerLimit( s ) );
331    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_LIMIT_TORRENT,       s->peerLimitPerTorrent );
332    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT,                tr_sessionGetPeerPort( s ) );
333    tr_bencDictAddBool( d, TR_PREFS_KEY_PEER_PORT_RANDOM_ON_START, s->isPortRandom );
334    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_LOW,     s->randomPortLow );
335    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_PORT_RANDOM_HIGH,    s->randomPortHigh );
336    tr_bencDictAddInt ( d, TR_PREFS_KEY_PEER_SOCKET_TOS,          s->peerSocketTOS );
337    if(s->peer_congestion_algorithm && s->peer_congestion_algorithm[0])
338        tr_bencDictAddStr ( d, TR_PREFS_KEY_PEER_CONGESTION_ALGORITHM, s->peer_congestion_algorithm );
339    tr_bencDictAddBool( d, TR_PREFS_KEY_PEX_ENABLED,              s->isPexEnabled );
340    tr_bencDictAddBool( d, TR_PREFS_KEY_PORT_FORWARDING,          tr_sessionIsPortForwardingEnabled( s ) );
341    tr_bencDictAddInt ( d, TR_PREFS_KEY_PREALLOCATION,            s->preallocationMode );
342    tr_bencDictAddReal( d, TR_PREFS_KEY_RATIO,                    s->desiredRatio );
343    tr_bencDictAddBool( d, TR_PREFS_KEY_RATIO_ENABLED,            s->isRatioLimited );
344    tr_bencDictAddBool( d, TR_PREFS_KEY_RENAME_PARTIAL_FILES,     tr_sessionIsIncompleteFileNamingEnabled( s ) );
345    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_AUTH_REQUIRED,        tr_sessionIsRPCPasswordEnabled( s ) );
346    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_BIND_ADDRESS,         tr_sessionGetRPCBindAddress( s ) );
347    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_ENABLED,              tr_sessionIsRPCEnabled( s ) );
348    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_PASSWORD,             tr_sessionGetRPCPassword( s ) );
349    tr_bencDictAddInt ( d, TR_PREFS_KEY_RPC_PORT,                 tr_sessionGetRPCPort( s ) );
350    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_URL,                  tr_sessionGetRPCUrl( s ) );
351    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_USERNAME,             tr_sessionGetRPCUsername( s ) );
352    tr_bencDictAddStr ( d, TR_PREFS_KEY_RPC_WHITELIST,            tr_sessionGetRPCWhitelist( s ) );
353    tr_bencDictAddBool( d, TR_PREFS_KEY_RPC_WHITELIST_ENABLED,    tr_sessionGetRPCWhitelistEnabled( s ) );
354    tr_bencDictAddBool( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_ENABLED, tr_sessionIsTorrentDoneScriptEnabled( s ) );
355    tr_bencDictAddStr ( d, TR_PREFS_KEY_SCRIPT_TORRENT_DONE_FILENAME, tr_sessionGetTorrentDoneScript( s ) );
356    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_ENABLED,        tr_sessionUsesAltSpeed( s ) );
357    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_UP_KBps,        tr_sessionGetAltSpeed_KBps( s, TR_UP ) );
358    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_DOWN_KBps,      tr_sessionGetAltSpeed_KBps( s, TR_DOWN ) );
359    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_BEGIN,     tr_sessionGetAltSpeedBegin( s ) );
360    tr_bencDictAddBool( d, TR_PREFS_KEY_ALT_SPEED_TIME_ENABLED,   tr_sessionUsesAltSpeedTime( s ) );
361    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_END,       tr_sessionGetAltSpeedEnd( s ) );
362    tr_bencDictAddInt ( d, TR_PREFS_KEY_ALT_SPEED_TIME_DAY,       tr_sessionGetAltSpeedDay( s ) );
363    tr_bencDictAddInt ( d, TR_PREFS_KEY_USPEED_KBps,              tr_sessionGetSpeedLimit_KBps( s, TR_UP ) );
364    tr_bencDictAddBool( d, TR_PREFS_KEY_USPEED_ENABLED,           tr_sessionIsSpeedLimited( s, TR_UP ) );
365    tr_bencDictAddInt ( d, TR_PREFS_KEY_UMASK,                    s->umask );
366    tr_bencDictAddInt ( d, TR_PREFS_KEY_UPLOAD_SLOTS_PER_TORRENT, s->uploadSlotsPerTorrent );
367    tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV4,        tr_ntop_non_ts( &s->public_ipv4->addr ) );
368    tr_bencDictAddStr ( d, TR_PREFS_KEY_BIND_ADDRESS_IPV6,        tr_ntop_non_ts( &s->public_ipv6->addr ) );
369    tr_bencDictAddBool( d, TR_PREFS_KEY_START,                    !tr_sessionGetPaused( s ) );
370    tr_bencDictAddBool( d, TR_PREFS_KEY_TRASH_ORIGINAL,           tr_sessionGetDeleteSource( s ) );
371}
372
373tr_bool
374tr_sessionLoadSettings( tr_benc * d, const char * configDir, const char * appName )
375{
376    int err = 0;
377    char * filename;
378    tr_benc fileSettings;
379    tr_benc sessionDefaults;
380    tr_benc tmp;
381    tr_bool success = FALSE;
382
383    assert( tr_bencIsDict( d ) );
384
385    /* initializing the defaults: caller may have passed in some app-level defaults.
386     * preserve those and use the session defaults to fill in any missing gaps. */
387    tr_bencInitDict( &sessionDefaults, 0 );
388    tr_sessionGetDefaultSettings( configDir, &sessionDefaults );
389    tr_bencMergeDicts( &sessionDefaults, d );
390    tmp = *d; *d = sessionDefaults; sessionDefaults = tmp;
391
392    /* if caller didn't specify a config dir, use the default */
393    if( !configDir || !*configDir )
394        configDir = tr_getDefaultConfigDir( appName );
395
396    /* file settings override the defaults */
397    filename = tr_buildPath( configDir, "settings.json", NULL );
398    err = tr_bencLoadFile( &fileSettings, TR_FMT_JSON, filename );
399    if( !err ) {
400        tr_bencMergeDicts( d, &fileSettings );
401        tr_bencFree( &fileSettings );
402    }
403
404    /* cleanup */
405    tr_bencFree( &sessionDefaults );
406    tr_free( filename );
407    success = (err==0) || (err==ENOENT);
408    return success;
409}
410
411void
412tr_sessionSaveSettings( tr_session    * session,
413                        const char    * configDir,
414                        const tr_benc * clientSettings )
415{
416    tr_benc settings;
417    char * filename = tr_buildPath( configDir, "settings.json", NULL );
418
419    assert( tr_bencIsDict( clientSettings ) );
420
421    tr_bencInitDict( &settings, 0 );
422
423    /* the existing file settings are the fallback values */
424    {
425        tr_benc fileSettings;
426        const int err = tr_bencLoadFile( &fileSettings, TR_FMT_JSON, filename );
427        if( !err )
428        {
429            tr_bencMergeDicts( &settings, &fileSettings );
430            tr_bencFree( &fileSettings );
431        }
432    }
433
434    /* the client's settings override the file settings */
435    tr_bencMergeDicts( &settings, clientSettings );
436
437    /* the session's true values override the file & client settings */
438    {
439        tr_benc sessionSettings;
440        tr_bencInitDict( &sessionSettings, 0 );
441        tr_sessionGetSettings( session, &sessionSettings );
442        tr_bencMergeDicts( &settings, &sessionSettings );
443        tr_bencFree( &sessionSettings );
444    }
445
446    /* save the result */
447    tr_bencToFile( &settings, TR_FMT_JSON, filename );
448
449    /* cleanup */
450    tr_free( filename );
451    tr_bencFree( &settings );
452}
453
454/***
455****
456***/
457
458/**
459 * Periodically save the .resume files of any torrents whose
460 * status has recently changed. This prevents loss of metadata
461 * in the case of a crash, unclean shutdown, clumsy user, etc.
462 */
463static void
464onSaveTimer( int foo UNUSED, short bar UNUSED, void * vsession )
465{
466    tr_torrent * tor = NULL;
467    tr_session * session = vsession;
468
469    if( tr_cacheFlushDone( session->cache ) )
470        tr_err( "Error while flushing completed pieces from cache" );
471
472    while(( tor = tr_torrentNext( session, tor )))
473        tr_torrentSave( tor );
474
475    tr_statsSaveDirty( session );
476
477    tr_timerAdd( session->saveTimer, SAVE_INTERVAL_SECS, 0 );
478}
479
480/***
481****
482***/
483
484static void tr_sessionInitImpl( void * );
485
486struct init_data
487{
488    tr_session  * session;
489    const char  * configDir;
490    tr_bool       done;
491    tr_bool       messageQueuingEnabled;
492    tr_benc     * clientSettings;
493};
494
495tr_session *
496tr_sessionInit( const char  * tag,
497                const char  * configDir,
498                tr_bool       messageQueuingEnabled,
499                tr_benc     * clientSettings )
500{
501    int64_t i;
502    tr_session * session;
503    struct init_data data;
504
505    assert( tr_bencIsDict( clientSettings ) );
506
507    tr_timeUpdate( time( NULL ) );
508
509    /* initialize the bare skeleton of the session object */
510    session = tr_new0( tr_session, 1 );
511    session->bandwidth = tr_bandwidthNew( session, NULL );
512    session->lock = tr_lockNew( );
513    session->cache = tr_cacheNew( 1024*1024*2 );
514    session->tag = tr_strdup( tag );
515    session->magicNumber = SESSION_MAGIC_NUMBER;
516    session->buffer = tr_valloc( SESSION_BUFFER_SIZE );
517    tr_bencInitList( &session->removedTorrents, 0 );
518
519    /* nice to start logging at the very beginning */
520    if( tr_bencDictFindInt( clientSettings, TR_PREFS_KEY_MSGLEVEL, &i ) )
521        tr_setMessageLevel( i );
522
523    /* start the libtransmission thread */
524    tr_netInit( ); /* must go before tr_eventInit */
525    tr_eventInit( session );
526    assert( session->events != NULL );
527
528    /* run the rest in the libtransmission thread */
529    data.done = FALSE;
530    data.session = session;
531    data.configDir = configDir;
532    data.messageQueuingEnabled = messageQueuingEnabled;
533    data.clientSettings = clientSettings;
534    tr_runInEventThread( session, tr_sessionInitImpl, &data );
535    while( !data.done )
536        tr_wait_msec( 100 );
537
538    return session;
539}
540
541static void turtleCheckClock( tr_session * s, struct tr_turtle_info * t );
542
543static void
544onNowTimer( int foo UNUSED, short bar UNUSED, void * vsession )
545{
546    int usec;
547    const int min = 100;
548    const int max = 999999;
549    struct timeval tv;
550    tr_torrent * tor = NULL;
551    tr_session * session = vsession;
552
553    assert( tr_isSession( session ) );
554    assert( session->nowTimer != NULL );
555
556    /**
557    ***  tr_session things to do once per second
558    **/
559
560    tr_timeUpdate( time( NULL ) );
561
562    if( session->turtle.isClockEnabled )
563        turtleCheckClock( session, &session->turtle );
564
565    while(( tor = tr_torrentNext( session, tor ))) {
566        if( tor->isRunning ) {
567            if( tr_torrentIsSeed( tor ) )
568                ++tor->secondsSeeding;
569            else
570                ++tor->secondsDownloading;
571        }
572    }
573
574    /**
575    ***  Set the timer
576    **/
577
578    /* schedule the next timer for right after the next second begins */
579    gettimeofday( &tv, NULL );
580    usec = 1000000 - tv.tv_usec;
581    if( usec > max ) usec = max;
582    if( usec < min ) usec = min;
583    tr_timerAdd( session->nowTimer, 0, usec );
584    /* fprintf( stderr, "time %zu sec, %zu microsec\n", (size_t)tr_time(), (size_t)tv.tv_usec ); */
585}
586
587static void loadBlocklists( tr_session * session );
588
589static void
590tr_sessionInitImpl( void * vdata )
591{
592    tr_benc settings;
593    struct init_data * data = vdata;
594    tr_benc * clientSettings = data->clientSettings;
595    tr_session * session = data->session;
596
597    assert( tr_amInEventThread( session ) );
598    assert( tr_bencIsDict( clientSettings ) );
599
600    dbgmsg( "tr_sessionInit: the session's top-level bandwidth object is %p",
601            session->bandwidth );
602
603    tr_bencInitDict( &settings, 0 );
604    tr_sessionGetDefaultSettings( data->configDir, &settings );
605    tr_bencMergeDicts( &settings, clientSettings );
606
607    assert( session->event_base != NULL );
608    session->nowTimer = evtimer_new( session->event_base, onNowTimer, session );
609    onNowTimer( 0, 0, session );
610
611#ifndef WIN32
612    /* Don't exit when writing on a broken socket */
613    signal( SIGPIPE, SIG_IGN );
614#endif
615
616    tr_setMessageQueuing( data->messageQueuingEnabled );
617
618    tr_setConfigDir( session, data->configDir );
619
620    session->peerMgr = tr_peerMgrNew( session );
621
622    session->shared = tr_sharedInit( session );
623
624    /**
625    ***  Blocklist
626    **/
627
628    {
629        char * filename = tr_buildPath( session->configDir, "blocklists", NULL );
630        tr_mkdirp( filename, 0777 );
631        tr_free( filename );
632        loadBlocklists( session );
633    }
634
635    assert( tr_isSession( session ) );
636
637    session->saveTimer = evtimer_new( session->event_base, 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    event_free( session->saveTimer );
1670    session->saveTimer = NULL;
1671
1672    event_free( session->nowTimer );
1673    session->nowTimer = NULL;
1674
1675    tr_verifyClose( session );
1676    tr_sharedClose( session );
1677    tr_rpcClose( &session->rpcServer );
1678
1679    /* Close the torrents. Get the most active ones first so that
1680     * if we can't get them all closed in a reasonable amount of time,
1681     * at least we get the most important ones first. */
1682    tor = NULL;
1683    n = session->torrentCount;
1684    torrents = tr_new( tr_torrent *, session->torrentCount );
1685    for( i = 0; i < n; ++i )
1686        torrents[i] = tor = tr_torrentNext( session, tor );
1687    qsort( torrents, n, sizeof( tr_torrent* ), compareTorrentByCur );
1688    for( i = 0; i < n; ++i )
1689        tr_torrentFree( torrents[i] );
1690    tr_free( torrents );
1691
1692    tr_cacheFree( session->cache );
1693    session->cache = NULL;
1694    tr_announcerClose( session );
1695    tr_statsClose( session );
1696    tr_peerMgrFree( session->peerMgr );
1697    tr_webClose( session, TR_WEB_CLOSE_WHEN_IDLE );
1698
1699    closeBlocklists( session );
1700
1701    tr_fdClose( session );
1702
1703    session->isClosed = TRUE;
1704}
1705
1706static int
1707deadlineReached( const time_t deadline )
1708{
1709    return time( NULL ) >= deadline;
1710}
1711
1712#define SHUTDOWN_MAX_SECONDS 20
1713
1714void
1715tr_sessionClose( tr_session * session )
1716{
1717    const time_t deadline = time( NULL ) + SHUTDOWN_MAX_SECONDS;
1718
1719    assert( tr_isSession( session ) );
1720
1721    dbgmsg( "shutting down transmission session %p... now is %zu, deadline is %zu", session, (size_t)time(NULL), (size_t)deadline );
1722
1723    /* close the session */
1724    tr_runInEventThread( session, sessionCloseImpl, session );
1725    while( !session->isClosed && !deadlineReached( deadline ) )
1726    {
1727        dbgmsg( "waiting for the libtransmission thread to finish" );
1728        tr_wait_msec( 100 );
1729    }
1730
1731    /* "shared" and "tracker" have live sockets,
1732     * so we need to keep the transmission thread alive
1733     * for a bit while they tell the router & tracker
1734     * that we're closing now */
1735    while( ( session->shared || session->web || session->announcer )
1736           && !deadlineReached( deadline ) )
1737    {
1738        dbgmsg( "waiting on port unmap (%p) or announcer (%p)... now %zu deadline %zu",
1739                session->shared, session->announcer, (size_t)time(NULL), (size_t)deadline );
1740        tr_wait_msec( 100 );
1741    }
1742
1743    tr_webClose( session, TR_WEB_CLOSE_NOW );
1744
1745    /* close the libtransmission thread */
1746    tr_eventClose( session );
1747    while( session->events != NULL )
1748    {
1749        static tr_bool forced = FALSE;
1750        dbgmsg( "waiting for libtransmission thread to finish... now %zu deadline %zu", (size_t)time(NULL), (size_t)deadline );
1751        tr_wait_msec( 500 );
1752        if( deadlineReached( deadline ) && !forced )
1753        {
1754            dbgmsg( "calling event_loopbreak()" );
1755            forced = TRUE;
1756            event_base_loopbreak( session->event_base );
1757        }
1758        if( deadlineReached( deadline+3 ) )
1759        {
1760            dbgmsg( "deadline+3 reached... calling break...\n" );
1761            break;
1762        }
1763    }
1764
1765    /* free the session memory */
1766    tr_bencFree( &session->removedTorrents );
1767    tr_bandwidthFree( session->bandwidth );
1768    tr_bitfieldDestruct( &session->turtle.minutes );
1769    tr_lockFree( session->lock );
1770    if( session->metainfoLookup ) {
1771        tr_bencFree( session->metainfoLookup );
1772        tr_free( session->metainfoLookup );
1773    }
1774    tr_free( session->torrentDoneScript );
1775    tr_free( session->buffer );
1776    tr_free( session->tag );
1777    tr_free( session->configDir );
1778    tr_free( session->resumeDir );
1779    tr_free( session->torrentDir );
1780    tr_free( session->downloadDir );
1781    tr_free( session->incompleteDir );
1782    tr_free( session->blocklist_url );
1783    tr_free( session->peer_congestion_algorithm );
1784    tr_free( session );
1785}
1786
1787tr_torrent **
1788tr_sessionLoadTorrents( tr_session * session,
1789                        tr_ctor    * ctor,
1790                        int        * setmeCount )
1791{
1792    int           i, n = 0;
1793    struct stat   sb;
1794    DIR *         odir = NULL;
1795    const char *  dirname = tr_getTorrentDir( session );
1796    tr_torrent ** torrents;
1797    tr_list *     l = NULL, *list = NULL;
1798
1799    assert( tr_isSession( session ) );
1800
1801    tr_ctorSetSave( ctor, FALSE ); /* since we already have them */
1802
1803    if( !stat( dirname, &sb )
1804      && S_ISDIR( sb.st_mode )
1805      && ( ( odir = opendir ( dirname ) ) ) )
1806    {
1807        struct dirent *d;
1808        for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
1809        {
1810            if( tr_str_has_suffix( d->d_name, ".torrent" ) )
1811            {
1812                tr_torrent * tor;
1813                char * path = tr_buildPath( dirname, d->d_name, NULL );
1814                tr_ctorSetMetainfoFromFile( ctor, path );
1815                if(( tor = tr_torrentNew( ctor, NULL )))
1816                {
1817                    tr_list_append( &list, tor );
1818                    ++n;
1819                }
1820                tr_free( path );
1821            }
1822        }
1823        closedir( odir );
1824    }
1825
1826    torrents = tr_new( tr_torrent *, n );
1827    for( i = 0, l = list; l != NULL; l = l->next )
1828        torrents[i++] = (tr_torrent*) l->data;
1829    assert( i == n );
1830
1831    tr_list_free( &list, NULL );
1832
1833    if( n )
1834        tr_inf( _( "Loaded %d torrents" ), n );
1835
1836    if( setmeCount )
1837        *setmeCount = n;
1838    return torrents;
1839}
1840
1841/***
1842****
1843***/
1844
1845void
1846tr_sessionSetPexEnabled( tr_session * session,
1847                         tr_bool      enabled )
1848{
1849    assert( tr_isSession( session ) );
1850
1851    session->isPexEnabled = enabled != 0;
1852}
1853
1854tr_bool
1855tr_sessionIsPexEnabled( const tr_session * session )
1856{
1857    assert( tr_isSession( session ) );
1858
1859    return session->isPexEnabled;
1860}
1861
1862tr_bool
1863tr_sessionAllowsDHT( const tr_session * session UNUSED )
1864{
1865    return tr_sessionIsDHTEnabled( session );
1866}
1867
1868tr_bool
1869tr_sessionIsDHTEnabled( const tr_session * session )
1870{
1871    assert( tr_isSession( session ) );
1872
1873    return session->isDHTEnabled;
1874}
1875
1876static void
1877toggleDHTImpl(  void * data )
1878{
1879    tr_session * session = data;
1880    assert( tr_isSession( session ) );
1881
1882    if( session->isDHTEnabled )
1883        tr_dhtUninit( session );
1884
1885    session->isDHTEnabled = !session->isDHTEnabled;
1886
1887    if( session->isDHTEnabled )
1888        tr_dhtInit( session, &session->public_ipv4->addr );
1889}
1890
1891void
1892tr_sessionSetDHTEnabled( tr_session * session, tr_bool enabled )
1893{
1894    assert( tr_isSession( session ) );
1895    assert( tr_isBool( enabled ) );
1896
1897    if( ( enabled != 0 ) != ( session->isDHTEnabled != 0 ) )
1898        tr_runInEventThread( session, toggleDHTImpl, session );
1899}
1900
1901static void
1902toggleLPDImpl(  void * data )
1903{
1904    tr_session * session = data;
1905    assert( tr_isSession( session ) );
1906
1907    if( session->isLPDEnabled )
1908        tr_lpdUninit( session );
1909
1910    session->isLPDEnabled = !session->isLPDEnabled;
1911
1912    if( session->isLPDEnabled )
1913        tr_lpdInit( session, &session->public_ipv4->addr );
1914}
1915
1916void
1917tr_sessionSetLPDEnabled( tr_session * session, tr_bool enabled )
1918{
1919    assert( tr_isSession( session ) );
1920    assert( tr_isBool( enabled ) );
1921
1922    if( ( enabled != 0 ) != ( session->isLPDEnabled != 0 ) )
1923        tr_runInEventThread( session, toggleLPDImpl, session );
1924}
1925
1926tr_bool
1927tr_sessionIsLPDEnabled( const tr_session * session )
1928{
1929    assert( tr_isSession( session ) );
1930
1931    return session->isLPDEnabled;
1932}
1933
1934tr_bool
1935tr_sessionAllowsLPD( const tr_session * session )
1936{
1937    return tr_sessionIsLPDEnabled( session );
1938}
1939
1940/***
1941****
1942***/
1943
1944void
1945tr_sessionSetCacheLimit_MB( tr_session * session, int max_bytes )
1946{
1947    assert( tr_isSession( session ) );
1948
1949    tr_cacheSetLimit( session->cache, toMemBytes( max_bytes ) );
1950}
1951
1952int
1953tr_sessionGetCacheLimit_MB( const tr_session * session )
1954{
1955    assert( tr_isSession( session ) );
1956
1957    return toMemMB( tr_cacheGetLimit( session->cache ) );
1958}
1959
1960/***
1961****
1962***/
1963
1964void
1965tr_sessionSetLazyBitfieldEnabled( tr_session * session,
1966                                  tr_bool      enabled )
1967{
1968    assert( tr_isSession( session ) );
1969
1970    session->useLazyBitfield = enabled != 0;
1971}
1972
1973tr_bool
1974tr_sessionIsLazyBitfieldEnabled( const tr_session * session )
1975{
1976    assert( tr_isSession( session ) );
1977
1978    return session->useLazyBitfield;
1979}
1980
1981/***
1982****
1983***/
1984
1985struct port_forwarding_data
1986{
1987    tr_bool enabled;
1988    struct tr_shared * shared;
1989};
1990
1991static void
1992setPortForwardingEnabled( void * vdata )
1993{
1994    struct port_forwarding_data * data = vdata;
1995    tr_sharedTraversalEnable( data->shared, data->enabled );
1996    tr_free( data );
1997}
1998
1999void
2000tr_sessionSetPortForwardingEnabled( tr_session  * session, tr_bool enabled )
2001{
2002    struct port_forwarding_data * d;
2003    d = tr_new0( struct port_forwarding_data, 1 );
2004    d->shared = session->shared;
2005    d->enabled = enabled;
2006    tr_runInEventThread( session, setPortForwardingEnabled, d );
2007}
2008
2009tr_bool
2010tr_sessionIsPortForwardingEnabled( const tr_session * session )
2011{
2012    assert( tr_isSession( session ) );
2013
2014    return tr_sharedTraversalIsEnabled( session->shared );
2015}
2016
2017/***
2018****
2019***/
2020
2021static int
2022tr_stringEndsWith( const char * str, const char * end )
2023{
2024    const size_t slen = strlen( str );
2025    const size_t elen = strlen( end );
2026
2027    return slen >= elen && !memcmp( &str[slen - elen], end, elen );
2028}
2029
2030static void
2031loadBlocklists( tr_session * session )
2032{
2033    int         binCount = 0;
2034    int         newCount = 0;
2035    struct stat sb;
2036    char      * dirname;
2037    DIR *       odir = NULL;
2038    tr_list *   list = NULL;
2039    const tr_bool   isEnabled = session->isBlocklistEnabled;
2040
2041    /* walk through the directory and find blocklists */
2042    dirname = tr_buildPath( session->configDir, "blocklists", NULL );
2043    if( !stat( dirname,
2044               &sb ) && S_ISDIR( sb.st_mode )
2045      && ( ( odir = opendir( dirname ) ) ) )
2046    {
2047        struct dirent *d;
2048        for( d = readdir( odir ); d; d = readdir( odir ) )
2049        {
2050            char * filename;
2051
2052            if( !d->d_name || d->d_name[0] == '.' ) /* skip dotfiles, ., and ..
2053                                                      */
2054                continue;
2055
2056            filename = tr_buildPath( dirname, d->d_name, NULL );
2057
2058            if( tr_stringEndsWith( filename, ".bin" ) )
2059            {
2060                /* if we don't already have this blocklist, add it */
2061                if( !tr_list_find( list, filename,
2062                                   (TrListCompareFunc)strcmp ) )
2063                {
2064                    tr_list_append( &list,
2065                                   _tr_blocklistNew( filename, isEnabled ) );
2066                    ++binCount;
2067                }
2068            }
2069            else
2070            {
2071                /* strip out the file suffix, if there is one, and add ".bin"
2072                  instead */
2073                tr_blocklist * b;
2074                const char *   dot = strrchr( d->d_name, '.' );
2075                const int      len = dot ? dot - d->d_name
2076                                         : (int)strlen( d->d_name );
2077                char         * tmp = tr_strdup_printf(
2078                                        "%s" TR_PATH_DELIMITER_STR "%*.*s.bin",
2079                                        dirname, len, len, d->d_name );
2080                b = _tr_blocklistNew( tmp, isEnabled );
2081                _tr_blocklistSetContent( b, filename );
2082                tr_list_append( &list, b );
2083                ++newCount;
2084                tr_free( tmp );
2085            }
2086
2087            tr_free( filename );
2088        }
2089
2090        closedir( odir );
2091    }
2092
2093    session->blocklists = list;
2094
2095    if( binCount )
2096        tr_dbg( "Found %d blocklists in \"%s\"", binCount, dirname );
2097    if( newCount )
2098        tr_dbg( "Found %d new blocklists in \"%s\"", newCount, dirname );
2099
2100    tr_free( dirname );
2101}
2102
2103static void
2104closeBlocklists( tr_session * session )
2105{
2106    tr_list_free( &session->blocklists,
2107                  (TrListForeachFunc)_tr_blocklistFree );
2108}
2109
2110void
2111tr_sessionReloadBlocklists( tr_session * session )
2112{
2113    closeBlocklists( session );
2114    loadBlocklists( session );
2115
2116    tr_peerMgrOnBlocklistChanged( session->peerMgr );
2117}
2118
2119int
2120tr_blocklistGetRuleCount( const tr_session * session )
2121{
2122    int       n = 0;
2123    tr_list * l;
2124
2125    assert( tr_isSession( session ) );
2126
2127    for( l = session->blocklists; l; l = l->next )
2128        n += _tr_blocklistGetRuleCount( l->data );
2129    return n;
2130}
2131
2132tr_bool
2133tr_blocklistIsEnabled( const tr_session * session )
2134{
2135    assert( tr_isSession( session ) );
2136
2137    return session->isBlocklistEnabled;
2138}
2139
2140void
2141tr_blocklistSetEnabled( tr_session * session,
2142                        tr_bool      isEnabled )
2143{
2144    tr_list * l;
2145
2146    assert( tr_isSession( session ) );
2147
2148    session->isBlocklistEnabled = isEnabled != 0;
2149
2150    for( l=session->blocklists; l!=NULL; l=l->next )
2151        _tr_blocklistSetEnabled( l->data, isEnabled );
2152}
2153
2154tr_bool
2155tr_blocklistExists( const tr_session * session )
2156{
2157    assert( tr_isSession( session ) );
2158
2159    return session->blocklists != NULL;
2160}
2161
2162int
2163tr_blocklistSetContent( tr_session * session, const char * contentFilename )
2164{
2165    tr_list * l;
2166    int ruleCount;
2167    tr_blocklist * b;
2168    const char * defaultName = DEFAULT_BLOCKLIST_FILENAME;
2169    tr_sessionLock( session );
2170
2171    for( b = NULL, l = session->blocklists; !b && l; l = l->next )
2172        if( tr_stringEndsWith( _tr_blocklistGetFilename( l->data ),
2173                               defaultName ) )
2174            b = l->data;
2175
2176    if( !b )
2177    {
2178        char * path = tr_buildPath( session->configDir, "blocklists", defaultName, NULL );
2179        b = _tr_blocklistNew( path, session->isBlocklistEnabled );
2180        tr_list_append( &session->blocklists, b );
2181        tr_free( path );
2182    }
2183
2184    ruleCount = _tr_blocklistSetContent( b, contentFilename );
2185    tr_sessionUnlock( session );
2186    return ruleCount;
2187}
2188
2189tr_bool
2190tr_sessionIsAddressBlocked( const tr_session * session,
2191                            const tr_address * addr )
2192{
2193    tr_list * l;
2194
2195    assert( tr_isSession( session ) );
2196
2197    for( l = session->blocklists; l; l = l->next )
2198        if( _tr_blocklistHasAddress( l->data, addr ) )
2199            return TRUE;
2200    return FALSE;
2201}
2202
2203void
2204tr_blocklistSetURL( tr_session * session, const char * url )
2205{
2206    if( session->blocklist_url != url )
2207    {
2208        tr_free( session->blocklist_url );
2209        session->blocklist_url = tr_strdup( url );
2210    }
2211}
2212
2213const char *
2214tr_blocklistGetURL ( const tr_session * session )
2215{
2216    return session->blocklist_url;
2217}
2218
2219
2220/***
2221****
2222***/
2223
2224static void
2225metainfoLookupInit( tr_session * session )
2226{
2227    struct stat  sb;
2228    const char * dirname = tr_getTorrentDir( session );
2229    DIR *        odir = NULL;
2230    tr_ctor *    ctor = NULL;
2231    tr_benc * lookup;
2232    int n = 0;
2233
2234    assert( tr_isSession( session ) );
2235
2236    /* walk through the directory and find the mappings */
2237    lookup = tr_new0( tr_benc, 1 );
2238    tr_bencInitDict( lookup, 0 );
2239    ctor = tr_ctorNew( session );
2240    tr_ctorSetSave( ctor, FALSE ); /* since we already have them */
2241    if( !stat( dirname, &sb ) && S_ISDIR( sb.st_mode ) && ( ( odir = opendir( dirname ) ) ) )
2242    {
2243        struct dirent *d;
2244        while(( d = readdir( odir )))
2245        {
2246            if( tr_str_has_suffix( d->d_name, ".torrent" ) )
2247            {
2248                tr_info inf;
2249                char * path = tr_buildPath( dirname, d->d_name, NULL );
2250                tr_ctorSetMetainfoFromFile( ctor, path );
2251                if( !tr_torrentParse( ctor, &inf ) )
2252                {
2253                    ++n;
2254                    tr_bencDictAddStr( lookup, inf.hashString, path );
2255                }
2256                tr_free( path );
2257            }
2258        }
2259        closedir( odir );
2260    }
2261    tr_ctorFree( ctor );
2262
2263    session->metainfoLookup = lookup;
2264    tr_dbg( "Found %d torrents in \"%s\"", n, dirname );
2265}
2266
2267const char*
2268tr_sessionFindTorrentFile( const tr_session * session,
2269                           const char       * hashString )
2270{
2271    const char * filename = NULL;
2272    if( !session->metainfoLookup )
2273        metainfoLookupInit( (tr_session*)session );
2274    tr_bencDictFindStr( session->metainfoLookup, hashString, &filename );
2275    return filename;
2276}
2277
2278void
2279tr_sessionSetTorrentFile( tr_session * session,
2280                          const char * hashString,
2281                          const char * filename )
2282{
2283    /* since we walk session->configDir/torrents/ to build the lookup table,
2284     * and tr_sessionSetTorrentFile() is just to tell us there's a new file
2285     * in that same directory, we don't need to do anything here if the
2286     * lookup table hasn't been built yet */
2287    if( session->metainfoLookup )
2288        tr_bencDictAddStr( session->metainfoLookup, hashString, filename );
2289}
2290
2291tr_torrent*
2292tr_torrentNext( tr_session * session,
2293                tr_torrent * tor )
2294{
2295    tr_torrent * ret;
2296
2297    assert( !session || tr_isSession( session ) );
2298
2299    if( !session )
2300        ret = NULL;
2301    else if( !tor )
2302        ret = session->torrentList;
2303    else
2304        ret = tor->next;
2305
2306    return ret;
2307}
2308
2309/***
2310****
2311***/
2312
2313void
2314tr_sessionSetRPCEnabled( tr_session * session,
2315                         tr_bool      isEnabled )
2316{
2317    assert( tr_isSession( session ) );
2318
2319    tr_rpcSetEnabled( session->rpcServer, isEnabled );
2320}
2321
2322tr_bool
2323tr_sessionIsRPCEnabled( const tr_session * session )
2324{
2325    assert( tr_isSession( session ) );
2326
2327    return tr_rpcIsEnabled( session->rpcServer );
2328}
2329
2330void
2331tr_sessionSetRPCPort( tr_session * session,
2332                      tr_port      port )
2333{
2334    assert( tr_isSession( session ) );
2335
2336    tr_rpcSetPort( session->rpcServer, port );
2337}
2338
2339tr_port
2340tr_sessionGetRPCPort( const tr_session * session )
2341{
2342    assert( tr_isSession( session ) );
2343
2344    return tr_rpcGetPort( session->rpcServer );
2345}
2346
2347void
2348tr_sessionSetRPCUrl( tr_session * session,
2349                     const char * url )
2350{
2351    assert( tr_isSession( session ) );
2352
2353    tr_rpcSetUrl( session->rpcServer, url );
2354}
2355
2356const char*
2357tr_sessionGetRPCUrl( const tr_session * session )
2358{
2359    assert( tr_isSession( session ) );
2360
2361    return tr_rpcGetUrl( session->rpcServer );
2362}
2363
2364void
2365tr_sessionSetRPCCallback( tr_session * session,
2366                          tr_rpc_func  func,
2367                          void *       user_data )
2368{
2369    assert( tr_isSession( session ) );
2370
2371    session->rpc_func = func;
2372    session->rpc_func_user_data = user_data;
2373}
2374
2375void
2376tr_sessionSetRPCWhitelist( tr_session * session,
2377                           const char * whitelist )
2378{
2379    assert( tr_isSession( session ) );
2380
2381    tr_rpcSetWhitelist( session->rpcServer, whitelist );
2382}
2383
2384const char*
2385tr_sessionGetRPCWhitelist( const tr_session * session )
2386{
2387    assert( tr_isSession( session ) );
2388
2389    return tr_rpcGetWhitelist( session->rpcServer );
2390}
2391
2392void
2393tr_sessionSetRPCWhitelistEnabled( tr_session * session,
2394                                  tr_bool      isEnabled )
2395{
2396    assert( tr_isSession( session ) );
2397
2398    tr_rpcSetWhitelistEnabled( session->rpcServer, isEnabled );
2399}
2400
2401tr_bool
2402tr_sessionGetRPCWhitelistEnabled( const tr_session * session )
2403{
2404    assert( tr_isSession( session ) );
2405
2406    return tr_rpcGetWhitelistEnabled( session->rpcServer );
2407}
2408
2409
2410void
2411tr_sessionSetRPCPassword( tr_session * session,
2412                          const char * password )
2413{
2414    assert( tr_isSession( session ) );
2415
2416    tr_rpcSetPassword( session->rpcServer, password );
2417}
2418
2419const char*
2420tr_sessionGetRPCPassword( const tr_session * session )
2421{
2422    assert( tr_isSession( session ) );
2423
2424    return tr_rpcGetPassword( session->rpcServer );
2425}
2426
2427void
2428tr_sessionSetRPCUsername( tr_session * session,
2429                          const char * username )
2430{
2431    assert( tr_isSession( session ) );
2432
2433    tr_rpcSetUsername( session->rpcServer, username );
2434}
2435
2436const char*
2437tr_sessionGetRPCUsername( const tr_session * session )
2438{
2439    assert( tr_isSession( session ) );
2440
2441    return tr_rpcGetUsername( session->rpcServer );
2442}
2443
2444void
2445tr_sessionSetRPCPasswordEnabled( tr_session * session,
2446                                 tr_bool      isEnabled )
2447{
2448    assert( tr_isSession( session ) );
2449
2450    tr_rpcSetPasswordEnabled( session->rpcServer, isEnabled );
2451}
2452
2453tr_bool
2454tr_sessionIsRPCPasswordEnabled( const tr_session * session )
2455{
2456    assert( tr_isSession( session ) );
2457
2458    return tr_rpcIsPasswordEnabled( session->rpcServer );
2459}
2460
2461const char *
2462tr_sessionGetRPCBindAddress( const tr_session * session )
2463{
2464    assert( tr_isSession( session ) );
2465
2466    return tr_rpcGetBindAddress( session->rpcServer );
2467}
2468
2469/****
2470*****
2471****/
2472
2473tr_bool
2474tr_sessionIsTorrentDoneScriptEnabled( const tr_session * session )
2475{
2476    assert( tr_isSession( session ) );
2477
2478    return session->isTorrentDoneScriptEnabled;
2479}
2480
2481void
2482tr_sessionSetTorrentDoneScriptEnabled( tr_session * session, tr_bool isEnabled )
2483{
2484    assert( tr_isSession( session ) );
2485    assert( tr_isBool( isEnabled ) );
2486
2487    session->isTorrentDoneScriptEnabled = isEnabled;
2488}
2489
2490const char *
2491tr_sessionGetTorrentDoneScript( const tr_session * session )
2492{
2493    assert( tr_isSession( session ) );
2494
2495    return session->torrentDoneScript;
2496}
2497
2498void
2499tr_sessionSetTorrentDoneScript( tr_session * session, const char * scriptFilename )
2500{
2501    assert( tr_isSession( session ) );
2502
2503    if( session->torrentDoneScript != scriptFilename )
2504    {
2505        tr_free( session->torrentDoneScript );
2506        session->torrentDoneScript = tr_strdup( scriptFilename );
2507    }
2508}
2509
2510/***
2511****
2512***/
2513
2514void
2515tr_sessionSetWebConfigFunc( tr_session * session, void (*func)(tr_session*, void*, const char* ) )
2516{
2517    session->curl_easy_config_func = func;
2518}
Note: See TracBrowser for help on using the repository browser.