source: trunk/libtransmission/session.c @ 12542

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

(trunk libT) remove unnecessary "UNUSED" attribute

  • Property svn:keywords set to Date Rev Author Id
File size: 71.3 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 12542 2011-07-12 12:24:41Z 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 )
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.