source: trunk/libtransmission/session.c @ 12514

Last change on this file since 12514 was 12514, checked in by jordan, 10 years ago

(trunk libt) #4315 "Transmission 2.31 crashes (segfaults) immediately after launch" -- remove the "max-open-files" code.

max-open-files might have been a nice configuration option once, but (1) we've never advertised it in the gui apps, and (2) the crazy cases are causing more trouble than this feature is worth. It's more complicated now after #4164 -- see #4294, #4311, and this ticket.

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