source: trunk/libtransmission/session.c @ 11956

Last change on this file since 11956 was 11956, checked in by jch, 11 years ago

Use large kernel buffers for the UDP socket when uTP is enabled.

Since we're using a single UDP socket to implement multiple uTP sockets,
and since we're not always timely in servicing an incoming UDP packet,
it's important to use a large receive buffer. The send buffer is probably
less critical, we increase it nonetheless.

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