source: trunk/libtransmission/session.c @ 12303

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

(trunk libT) when reading piece data in from a socket, avoid two unnecessary calls to memcpy()

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