source: trunk/libtransmission/session.c @ 11752

Last change on this file since 11752 was 11752, checked in by jordan, 11 years ago

(trunk libT) #3906 "DHT ignores bind-address-ipv6" -- make the process of getting the binding address consistent between IPv4 and IPv6 sockets.

As suggested by Juiusz after the IPv6 binding commit in r11749. See: https://trac.transmissionbt.com/ticket/3906#comment:6

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